HTTP API error codes
Error responses from the orchestrator’s HTTP endpoints.
Ticket comments
Status and progress comments AI-Implement posts on your issue.
Blocker reasons
Why an eligible issue was not dispatched.
Job status values
The states a dispatch moves through, end to end.
Run failure messages
Common planning and implementation failures, and how to fix them.
Issue lifecycle
The state diagram of triggers and transitions.
HTTP API error codes
The orchestrator’s HTTP endpoints return anerror field on any failed request. The tables below list the errors each endpoint can return, grouped by who calls it: integration and callback endpoints used by runners and automation, and the admin API behind the admin UI.
Response body shapes
A failed request returns a JSON body with a singleerror field:
The error identifier that varies by endpoint.
- For
/runner/*and/trigger/gap-fill, a stable slug code (for example,missing_bearer). - For
/api/*and the webhook, a human-readable message (for example,Invalid JSON body).
Some responses add fields alongside
error, noted per endpoint below.Integration and callback API
These endpoints are called by runners (GitHub Actions jobs, Fly Machine sessions, or local Docker containers) and by external automation, not from the admin UI. They do not use the admin session — each authenticates with a token or nonce tied to the run or trigger.POST /runner/result
A runner reports the final outcome of a dispatch: success with a PR, or failure with error context.
| HTTP | error | Condition |
|---|---|---|
| 400 | invalid_body | Body is missing or not a JSON object |
| 400 | invalid_comment_shape | A comment entry is not an object with a string body |
| 400 | invalid_comments | comments is missing or not an array |
| 400 | invalid_outcome | outcome is not success or failure |
| 400 | invalid_phase | phase is not planning, implementation, or gap-analysis |
| 400 | missing_prUrl | A successful implementation result did not include a PR URL |
| 400 | phase_mismatch | The token’s phase does not match the body |
| 401 | bad_signature, expired, malformed, wrong_audience | The run token has a bad signature, is expired, is malformed, or is for the wrong endpoint |
| 401 | missing_bearer | Missing or malformed Authorization: Bearer header |
| 409 | already_consumed | The single-use token was already consumed |
POST /runner/progress
A runner streams step-by-step progress during a run.
| HTTP | error | Condition |
|---|---|---|
| 400 | invalid_body | Body is missing or not a JSON object |
| 400 | invalid_step_id | step.id is missing or not a string |
| 400 | invalid_step_started_at | step.started_at is missing or not a string |
| 400 | invalid_step_status | step.status is missing or not a string |
| 400 | invalid_step_type | step.type is missing or not a string |
| 400 | step_required | step is missing or not an object |
| 401 | bad_signature, expired, malformed, wrong_audience | The progress token has a bad signature, is expired, is malformed, or is for the wrong endpoint |
| 401 | missing_bearer | Missing or malformed Authorization: Bearer header |
| 404 | job_not_found | No run matches the token |
GET /runner/planning-context
A runner fetches the planning context for its run.
| HTTP | error | Condition |
|---|---|---|
| 401 | bad_signature, expired, malformed, wrong_audience | The progress token has a bad signature, is expired, is malformed, or is for the wrong endpoint |
| 401 | missing_bearer | Missing or malformed Authorization: Bearer header |
POST /trigger/gap-fill
External automation triggers a gap-fill run on an existing pull request.
| HTTP | error | Condition |
|---|---|---|
| 400 | issueKey and positive integer prNumber required | issueKey is missing, or prNumber is not a positive integer |
| 401 | unauthorized | Missing bearer token, or it does not match the trigger secret |
| 404 | mapping_not_found | No configured mapping owns an issue with this key |
| 423 | project_paused | The owning mapping is paused (response also includes teamKey) |
| 501 | Gap fill trigger not configured | The gap-fill trigger is not configured on this orchestrator |
| 502 | dispatch_failed | The workflow dispatch to GitHub failed (response also includes detail and dispatchStatus) |
POST /api/token
A session requests a short-lived GitHub token, issued against a one-time nonce.
| HTTP | error | Condition |
|---|---|---|
| 400 | nonce and owner are required | nonce or owner is missing |
| 403 | Invalid or expired nonce | No active job matches the nonce |
| 403 | Owner mismatch | The owner does not match the job’s repository owner |
| 500 | Failed to generate token | The body was not valid JSON, or token generation failed |
POST /api/status
A session posts a lifecycle status event, which becomes a comment on the issue.
| HTTP | error | Condition |
|---|---|---|
| 400 | event is required | event is missing or not a string |
| 400 | Invalid JSON body | Body could not be parsed as JSON |
| 400 | nonce is required | nonce is missing or not a string |
| 400 | Unknown event type: {type} | event is not an allowed remote event type |
| 403 | Invalid or expired nonce | No active job matches the nonce |
| 500 | Internal server error | An unexpected error occurred (for example, posting the status comment failed) |
POST /api/step-report
A runner reports the status of a single pipeline step.
| HTTP | error | Condition |
|---|---|---|
| 400 | nonce is required | nonce is missing or not a string |
| 400 | Invalid JSON body | Body could not be parsed as JSON |
| 400 | step is required | step is missing or not an object |
| 400 | step.id is required and must be a string | A required step field (id, type, status, started_at) is missing or not a string |
| 403 | Invalid or expired nonce | No active job matches the nonce |
| 500 | Internal server error | An unexpected error occurred while persisting the step |
POST /api/github/webhook
GitHub notifies the orchestrator of pull-request events.
Events that don’t match a merged pull request return
200 and are ignored.| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid JSON payload | Body could not be parsed as JSON |
| 400 | Missing required PR fields | The merged-PR payload is missing a required field (number, URL, branch, merge-commit SHA, or repository) |
| 401 | Invalid signature | The X-Hub-Signature-256 did not verify against the webhook secret |
Admin API
These endpoints support the admin UI. See Admin UI for what each panel does.Every admin endpoint below requires a valid admin session except
POST /api/auth.Requests without one return 401 with Unauthorized.POST /api/auth
Exchange the admin access code for a session token.
| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid request body | Body could not be parsed as JSON |
| 403 | Invalid access code | The access code does not match |
Job steps and issue lists
GET /api/jobs/{jobId}/stepsreturns a job’s step historyGET /api/issueslists tracked issuesGET /api/blockerslists issues currently blocked from dispatch
| HTTP | error | Condition |
|---|---|---|
| 404 | job not found | No job with that ID |
| 502 | (underlying error message) | Fetching issues from the ticketing provider failed |
POST /api/mappings
Create or update a project mapping.
Every field validation returns
400 with a message naming the problem.| HTTP | error | Condition |
|---|---|---|
| 400 | defaultBranch is required | No default branch was given, and the mapping has none set |
| 400 | Invalid request body | Body could not be parsed as JSON |
| 400 | teamKey, owner, and repo are required | One of these required fields is missing |
| HTTP | error | Condition |
|---|---|---|
| 400 | awsRegion is required when provider is 'bedrock' | provider is bedrock but no AWS region was given |
| 400 | provider 'bedrock' is not supported with executionMode 'fly-machines' | bedrock was combined with fly-machines |
| 400 | provider must be 'anthropic' or 'bedrock' | provider is some other value |
| HTTP | error | Condition |
|---|---|---|
| 400 | executionMode must be 'github-actions' or 'fly-machines' | executionMode is some other value |
| 400 | machineCpus must be a positive integer | machineCpus is below 1 or not an integer |
| 400 | machineMemoryMb must be an integer >= 256 | machineMemoryMb is below 256 or not an integer |
| 400 | sessionMode must be 'autonomous', 'interactive', or 'hybrid' | sessionMode is some other value |
| HTTP | error | Condition |
|---|---|---|
| 400 | branchPrefix invalid: {detail} | The branch prefix is invalid |
| 400 | extraEnv must be a plain object | extraEnv is set but is not an object |
| 400 | extraEnv values must all be strings | An extraEnv value is not a string |
| 400 | maxInProgressAiIssues must be a positive integer | maxInProgressAiIssues is below 1 or not an integer |
| 400 | planningWorkflowFile is required when planningEnabled is true | Planning is enabled but no planning workflow file was given |
| 400 | (ticketing validation message) | The ticketing provider configuration is invalid |
Mapping changes
PATCH /api/mappings/{teamKey}updates a mapping’s concurrency cap or paused stateDELETE /api/mappings/{teamKey}removes a mapping
| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid request body | Body could not be parsed as JSON |
| 400 | maxInProgressAiIssues must be a positive integer | The new cap is below 1 or not an integer |
| 400 | Specify either paused or maxInProgressAiIssues, not both | The request set both fields at once |
| 404 | Team not found | No mapping with that team key |
DELETE returns 404 with { "deleted": false } (no error field) when the mapping does not exist.POST /api/mappings/{teamKey}/sync-workflows
Re-sync workflow templates into the mapping’s repository.
| HTTP | error | Condition |
|---|---|---|
| 404 | Team not found | No mapping with that team key |
| 500 | (underlying error message) | The sync to GitHub failed |
Jira configuration
POST /api/jira/validate-jqlvalidates a JQL clauseGET /api/jira/fieldslists Jira fieldsGET /api/jira/field-optionslists the options for a Jira field
| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid JSON body | The validate-jql body could not be parsed |
| 400 | fieldId query param required | /api/jira/field-options was called without fieldId |
| 400 | jql field required | The validate-jql body omitted jql |
| 400 | (validation message) | The JQL clause is invalid |
| 500 | (underlying error message) | The Jira request failed |
| 501 | Jira not configured | No Jira connection is configured on this orchestrator |
/api/jira/field-options returns an empty list, not an error, when the field has no selectable options.POST /api/runner-mode
Override the global runner execution mode.
| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid request body | Body could not be parsed as JSON |
| 400 | mode must be one of: default, gha, fly, local, shadow | mode is not a recognized value |
| 409 | RUNNER_MODE env var is set; persisted to DB but has no effect at runtime until the env var is unset | The value was saved, but a RUNNER_MODE environment variable overrides it at runtime |
Sessions
GET /api/sessionslists active sessionsDELETE /api/sessions/{machineId}destroys a session
| HTTP | error | Condition |
|---|---|---|
| 500 | (underlying error message) | Listing or destroying the session failed |
| 503 | Fly sessions config not set | Destroying a session while Fly sessions are not configured |
GET /api/sessions returns an empty list, not an error, when Fly sessions are not configured.Secrets
/api/mappings/{teamKey}/secretslists, sets, and deletes per-team secrets/api/global-secretslists, sets, and deletes global secrets
| HTTP | error | Condition |
|---|---|---|
| 400 | Invalid request body | Body could not be parsed as JSON |
| 400 | name and value are required | The secret name is missing, or the value is missing or empty |
| 400 | name must contain only letters, digits, and underscores | The secret name contains other characters |
| 400 | Secret name must not start with a team key prefix (...) | A global secret name collides with a team prefix |
| 404 | Secret not found | The named secret does not exist (on delete) |
| 404 | Team not found | No mapping with that team key (per-team secrets) |
| 500 | (underlying error message) | The secret operation failed |
| 503 | Fly sessions config not set | Secrets require Fly sessions to be configured |
Ticket comments
AI-Implement comments on the issue it is working so you can follow a run from your issue tracker, without watching the admin UI or the GitHub Actions logs. There are two kinds: progress comments posted at each stage of a session run, and outcome comments posted when a run opens a PR or fails.Progress comments
When a run executes in a session (Fly Machines or local Docker), AI-Implement posts a comment at each stage:| Comment | When |
|---|---|
| 🚀 Session machine created. Cloning repo and running setup… | A session machine is provisioned |
| ✅ Environment ready. Claude is implementing… | The repository is cloned and dependencies are installed |
| 📝 Claude finished. PR # opened: | Claude finishes and opens a pull request |
| 🧪 Running verification script… | The verification script starts |
| ✅ Verification passed | The verification script passes |
| ❌ Verification failed: | The verification script fails |
| 🧹 Session machine cleaned up. Duration: | The session machine is torn down |
| ⚠️ Session failed: . Machine will be cleaned up. | The run errors out |
| ⚠️ Session timed out: . Machine will be cleaned up. | The run exceeds its time budget |
Each comment also includes a timestamp, and a link to the machine logs when one is available.
Outcome comments
These report how a run finished:- A pull-request link, posted when the run opens a PR.
- A failure notice when a run errors out:
⚠️ Planning failed: {reason}or⚠️ Implementation failed: {reason}. - A log excerpt, posted on a terminal failure or timeout and labeled with the context below.
{reason} is the underlying failure message from the run.See Run failure messages for what these messages contain and how to resolve them.| Log label | Meaning |
|---|---|
| container_failed | The local Docker container exited with an error |
| container_timeout | The local Docker session exceeded its lifetime |
| machine_timeout | The session machine exceeded its lifetime |
| post_push_review_not_approved | A PR was opened, but the post-push review flagged blockers |
| pr_not_found | The run finished but did not open a PR |
| session_failed | The session run failed |
Blocker reasons
The orchestrator checks for eligible issues on a schedule. When it skips one, the Blockers view in the admin UI shows why. Most reasons clear on their own once the condition changes — a slot frees up, or the dedup window passes — butno-mapping needs you to add a project mapping.
| Reason | Message shown | When |
|---|---|---|
concurrency | at concurrency cap (/). Waiting for a slot. | The team is at its maximum number of in-progress issues |
dedup | Already dispatched recently. Waiting for the in-flight job. | The issue was dispatched within the last 24 hours |
no-mapping | No mapping for team . Add one in Projects. | The issue’s team has no project mapping |
An issue is also skipped if it has open blocking issues linked to it. That is a separate eligibility check, not one of the reasons above.
Job status values
Each dispatch moves through these statuses, shown in the dispatch log and the issue list. Non-terminal statuses describe a run still in flight; terminal statuses are the final outcome and do not change afterward.| Status | Terminal | Meaning |
|---|---|---|
unknown | No | Initial state, before a run is linked |
dispatched | No | The workflow was dispatched; awaiting a run |
running | No | The run is in progress |
completed | Yes | The run succeeded and a PR was opened |
review_failed | Yes | A PR was opened, but the post-push review flagged blockers |
failed | Yes | The run errored, or finished without opening a PR |
timed_out | Yes | The run exceeded its time budget |
timed_out status carries one of these reasons:
| Reason | Meaning |
|---|---|
container_timeout | The local Docker session exceeded its lifetime |
machine_timeout | The session machine exceeded its lifetime (around 60 minutes) |
run_not_found | A run was never linked within the time limit (around 10 minutes) |
timed_out | The GitHub Actions run reported a timeout |
Run failure messages
When a planning or implementation run fails, AI-Implement posts the reason on the issue as⚠️ Planning failed: {reason} or ⚠️ Implementation failed: {reason}. The {reason} pairs a short note about which step failed with the underlying error from the tool involved (Claude, git, or the package manager).
The most common failures are listed below.
Reached max turns
The run stopped at the per-run turn limit before finishing
Reached max turns
The run stopped at the per-run turn limit before finishing
Example:
Implementation failed: LLM invocation failed with exit code 1 … Reached max turns (50)Claude stopped because it reached the maximum number of turns allowed for a single run — the work was too large to finish in the limit, or the run got stuck repeating steps.See Troubleshooting for information on how to raise the limit.The Claude run failed
Claude ended with an error before finishing
The Claude run failed
Claude ended with an error before finishing
Example:
Implementation failed: LLM invocation failed with exit code 1: …Claude’s run ended with an error rather than completing — for example an API error or a loss of context. The exit code and the end of the message point to the underlying cause.No changes were produced
Claude finished without editing any files
No changes were produced
Claude finished without editing any files
Example:
Implementation failed: Nothing to commit: Claude left no file changes in the working treeClaude finished without changing any files, so there was nothing to open a pull request with. The issue may already be addressed, or may need more detail before a re-run.Pushing the branch or opening the PR failed
The push or pull-request creation was rejected
Pushing the branch or opening the PR failed
The push or pull-request creation was rejected
Example:
Implementation failed: PR creation failed with HTTP 422: …AI-Implement could not push the branch or open the pull request — commonly branch protection rules, missing repository permissions, or a pull request already open for the branch.Cloning the repository failed
The target repository could not be checked out
Cloning the repository failed
The target repository could not be checked out
Example:
Implementation failed: git clone failed (exit 128): …The runner could not clone or check out the target repository — commonly repository access or permissions, or a missing branch.Issue lifecycle
AI-Implement moves each issue through a fixed set of stages. The diagram shows the stages and what moves an issue between them.See Triggers and status indicators for how each stage maps to a Linear label or Jira status, and how to re-run after a failure.
| Stage | Linear label | Jira status |
|---|---|---|
| Triggered | AI-Implement (you add) | Ready |
| Planning | AI-Planning | Planning |
| Plan complete | Plan-Complete | Plan Approved |
| Implementing | AI-Working | Implementing |
| PR opened | Ready for Review | PR Ready |
| Planning failed | AI labels removed, plus a comment | Planning Failed |
| Implementation failed | AI labels removed, plus a comment | Implementation Failed |