Troubleshooting
Most loopy-loop problems fall into a handful of categories: a loop that will not stop, a broken evaluator, a coordinator that refuses to start, or a worker that cannot reach it. This page is organized as symptom, cause, and fix so you can jump to the one you are hitting. When a fix depends on a deeper contract, it links to the page that explains it.
The loop will not stop
Symptom. Evals look like they pass, or the goal clearly looks met, but the loop keeps assigning new iterations.
Cause. goal_check.json does not stop the loop. It is per-iteration evidence
only. The single switch that stops a run is the session-scoped control.json.
Fix. Make sure a workflow actually writes state: "stopped" with a valid
stop_reason (goal_met or unresolvable_error) to control.json:
{
"state": "stopped",
"reason": "Accepted evidence satisfies the goal.",
"stop_reason": "goal_met",
"schema_version": 1
}If you expected an eval workflow to stop the loop, confirm three things: it has
emits_goal_check: true, it writes valid JSON to the exact goal_check.json
output path, and it updates control.json when the goal is met. The eval verdict
is evidence; the stop is a separate, explicit decision. See
Success & Control for the full model.
You can always force a stop by hand with loopy stop, which the loop records as
stop_reason: "stop_requested".
The loop stopped with goal_check_broken
Symptom. The run ended early with stop_reason: "goal_check_broken".
Cause. A goal_check-emitting workflow repeatedly failed to write a valid
goal_check.json. The coordinator counts consecutive failures and, once they reach
goal_check_consecutive_failures_cap (default 3), stops rather than looping on a
broken evaluator.
Fix. Open the recent iterations/<NNNN>_<workflow_id>/ directories and check
whether goal_check.json is missing or malformed. The required v1 schema is:
{
"goal_met": false,
"reason": "CTA exists, but deployment docs are still missing.",
"schema_version": 1
}Fix the workflow prompt so it writes valid JSON to the correct path every run. A
single valid goal_check.json resets the failure counter. See
Evaluation for how the eval workflows should produce it.
The loop stopped with invalid_control_output
Symptom. The run ended with stop_reason: "invalid_control_output".
Cause. control.json existed but could not be parsed as a valid control signal
— malformed JSON, a wrong schema_version, or missing fields.
Fix. Restore control.json to a valid shape. While the loop is meant to keep
running it should read:
{
"state": "running",
"reason": "session active",
"stop_reason": null,
"schema_version": 1
}Make sure the workflow that writes control.json emits exactly this schema and only
ever sets stop_reason to goal_met or unresolvable_error.
Coordinator refuses to start (preflight fails)
The coordinator runs a preflight check before it will start. Several distinct errors surface here:
- API key not exported. Export the environment variable named in
team_harness_api_key_env(oftenOPENROUTER_API_KEY) in both the coordinator shell and every worker shell. Some providers, such ascodex, skip the API-key check and use local auth instead. - Unknown config field. The config parser rejects unknown keys. Every
team_harness_*field name is exact — check spelling against Configuration. - Workflow id collides with
goal_check.goal_checkis reserved. Pick a different id for your own workflow. must_followreferences a missing workflow. The referenced id must match a folder under.loopy_loop/workflow_sets/<workflow_set>/workflows/.run_after_successes.workflow_idreferences a missing workflow. Same rule — the id must match an existing workflow folder.
Re-running the coordinator against a running session
Symptom. Starting the coordinator fails with a message about existing running state.
Cause. This is intentional. If the previous coordinator left the loop in a
non-terminal (running) state — for example because it was killed without reaching
a stop — starting fresh over it could corrupt the run.
Fix. Reattach to the live session instead of starting over:
loopy coordinator --host 127.0.0.1 --port 8080 --resumeIf you would rather end that session cleanly first, run loopy stop to drive it to
a terminal state, then start a fresh coordinator without --resume. On startup, a
coordinator archives an already-terminal state and starts fresh; a non-terminal
state requires --resume. See CLI Reference for the flags.
Killing only the coordinator leaves state "running"
Symptom. You stopped the coordinator process, and now loopy status still shows
the session as running.
Cause. Killing the coordinator does not write a terminal state; the session
stays running on disk.
Fix. Either pass --resume the next time you start the coordinator to reattach,
or run loopy stop first to reach a terminal state before a fresh start.
A worker crashed mid-iteration
Symptom. A worker process died in the middle of an assignment, and you are not sure whether that iteration's work was lost or will be double-counted.
Cause. The worker crashed inside the handoff window — after writing its result
but before the coordinator acknowledged /finished.
Fix. Usually nothing. The loop is built to recover: when the next /register
arrives, the coordinator looks in the current iteration directory for
pending_finished_request.json (or, failing that, result.json). If either proves
the task completed, it records the completed result instead of marking the task
abandoned. Only a task with no recoverable local result is recorded as failed with
error="abandoned". Just start a worker again and let it re-register. The
HTTP Contract documents the recovery rules in full.
The worker cannot reach the coordinator
Symptom. The worker errors on connect, or nothing happens after you start it.
Cause. The worker's --coordinator URL is wrong, the coordinator is not
listening on that host/port, or the two processes do not share the environment they
need.
Fix. Confirm the coordinator is up on the host and port you started it with, and point the worker at the same address:
loopy worker --coordinator http://127.0.0.1:8080Also export the team_harness_api_key_env variable in the worker shell, not just
the coordinator shell — each process resolves the key independently. The worker
calls /register once for its first task, then loops on /finished until it
receives action: "stop"; multiple workers may share one coordinator.
A workflow ignores project_state
Symptom. A workflow does not read or update the project_state/ files you
expected it to.
Cause. The runtime only injects the session paths into the rendered prompt; it does not parse markdown state or enforce any ownership rules.
Fix. State the contract explicitly in the workflow prompt — which
project_state/ files to read, which to update, and who owns each. See
Session Layout for the conventional file set and ownership
rules that reusable workflows follow.