Skip to main content
Run outerlayer doctor first. Most failing checks name their fix. The entries below are grouped by the message or symptom you see. A build that fails or never starts on a host is covered in Troubleshoot builds.

Signing in and choosing a factory

In a terminal, login prints a link and a code, then waits. Open the link, check the dashboard shows the same code, and approve it. The link has a time limit. After it, you see the login link expired; run outerlayer login again. A login someone declined ends with the login was denied. When stdout is not a terminal, as in an agent’s shell, login prints one JSON line with "status": "waiting" and exits 75. Approve the link it names, then run the check command from that line, outerlayer login --check <id>. Exit 75 again means it is still waiting. See outerlayer login.

login exits 2 with “—url and —app-id apply only to a piped key”

Browser login takes its gateway address from the dashboard, so it refuses both flags. Drop them, or pipe a key in with them.

login says it “is waiting for an API key on stdin”

Something left stdin open, so login waits for a key. Close stdin, or run it with nothing piped to sign in by browser. The connection is saved, but the repository is not linked to the factory. The message prints the factory’s Work page link. Open it, choose Connect GitHub repository and grant the App access, then Link repository. Run outerlayer connect again. See Connect a repository.

A command says no gateway URL or address is saved

There is no default gateway, so a cloud command with no URL refuses. You see missing --url, no gateway address is saved, or, from mcp serve, no gateway URL. Run outerlayer login, which saves the address from the dashboard. Or set OUTERLAYER_URL, or pass --url. See Credential resolution.

A command says credentials are missing

sync, work and emit refuse when no credential is saved. The message starts with missing, then names each value it lacks, such as missing --url, a login or OUTERLAYER_API_KEY, --app-id. Run outerlayer login with nothing piped and approve the link. Then run outerlayer connect to choose a factory. If it says you are logged in but no factory is chosen, run outerlayer connect, or pass --app-id. To use a key you already have, pipe it in:
Never pass the key on the command line. The --api-key flag is deprecated. Pipe the key to login, or set OUTERLAYER_API_KEY. See API keys.

Commands use a factory key instead of your login

A factory key always wins over a login, whether it comes from OUTERLAYER_API_KEY or apiKey in ~/.outerlayer/config.json. Run outerlayer doctor. A Factory key check means a key is in use, and its detail names where it came from. To use your login instead:
  • unset OUTERLAYER_API_KEY, and remove it from your shell profile.
  • Delete the apiKey field from ~/.outerlayer/config.json.
After a browser login, login warns when an apiKey is saved in that file. It does not warn about OUTERLAYER_API_KEY.

”not authorized — the API key is unknown, expired, or bound to a different factory”

The key was revoked, or the factory id you gave login is a different factory. Check Settings → General for the Factory Id and Settings → API keys for the key. Then run login again. With a login, the message says the login expired or was revoked: run outerlayer login.

Sessions and sync

sync says “not launched with OUTERLAYER_WORK”

The sessions were started without the variable. They stay on this machine and never upload. Start a new session on the item’s number. work build --issue adds the issue to the Work page if it is not there yet, and prints the item’s number:
See Launch a session.

outerlayer sync exits 2

The server rejected at least one session. The first 10 rejects are listed under the summary, then … and N more rejects. Each shows its reason, such as session belongs to another work item. A rejected session is not retried. Fix the cause, then run outerlayer sync --all to send it again. See outerlayer sync.

The session started but it is not on the work item’s page

Read ~/.outerlayer/spool/hook-errors.log. The usual reasons:
  • OUTERLAYER_WORK named an issue (#42) rather than an item number. Run work build --issue 42 --local to get the item’s number, then launch on it.
  • The number names no item on this factory, or a withdrawn one. Check it on the Work page.
  • The gateway could not be reached. The link retries a few times, then gives up.
Start a new session once the cause is fixed. A running session cannot be linked afterwards.

Cursor sessions are not captured

Reading Cursor’s chats needs Node 22.5 or later. On older Node, sync skips Cursor and still syncs every other agent. Upgrade Node. See Supported agents.

The status line shows only the session cost

The daemon is not running. The cross-agent total comes from a file it keeps fresh. Start it in a terminal you keep open, or under a login agent:

Hooks stopped firing after a Claude Code update

Run outerlayer doctor and read Hooks installed and Settings JSON valid. Hooks installed also fails when a hook runs a file that no longer exists, such as a cleared npx cache. Install the CLI globally, then reinstall the hooks. init backs the settings file up first.

Work items and review

work build says the repository “is not a workpiece of this factory”

The error code is repository_not_connected. outerlayer work build --issue <n> uses the checkout’s remote, and that repository must be linked to the factory. Link it on the factory’s Settings → General, or from the setup panel on the Work page. Or pass --repo owner/name for a repository that is linked. See Connect a repository.

work claim is refused with work_item_not_requested

An implement claim needs an unused build request. Ask for one with outerlayer work build --issue <n>, then claim again. An amend claim needs a thread waiting on an agent since the last attempt. Hand a thread to the agent on the item’s page first. See work claim.

work comment is refused with verdict_needs_person, verdict_on_criterion or artifact_needs_attach

  • verdict_needs_person: --pass and --fail are a person’s judgment. Run them yourself, outside the agent session, with your own login, not a key.
  • verdict_on_criterion: a verdict is on the whole item. Drop --criterion, and leave a note on a criterion with a plain comment.
  • artifact_needs_attach: --artifact is accepted only with --attach and --criterion. Nothing was stored.
See work comment.

emit criteria is refused (409)

The item already has a recorded list, and only a person may replace it. Run the command yourself, outside the session, with your own login. To let sessions replace it, set criteria: { replace: anyone } in .outerlayer/policy.yaml on the default branch. See emit criteria.

No checks run on your pull requests

The factory reads .outerlayer/ from the default branch. Until init’s files are merged there, no check runs. Commit .outerlayer/ and merge it. See the Quickstart.

An artifact refuses to emit inside a session

The session has no launch record, so a spooled artifact would never upload. Start the session with OUTERLAYER_WORK, or run the emit from a plain shell with --pr.

The evidence comment shows a policy error row

.outerlayer/policy.yaml or a validator file on the base branch failed to load. The row names the file and the problem. A validator loads whole or not at all. See Policy and validators.

Hosts and the GitHub App

outerlayer doctor says the installation “has not accepted” a permission

Builds need a permission the GitHub App installation has not accepted yet. An owner of the installation accepts it on the settings page doctor links. If doctor says the App does not request it, the App’s own settings must add it first. See Check what each repository is missing.

A runner key is refused on a new machine

A runner key is bound to the host key of the first machine that used it. Elsewhere, the runner reports the key is “bound to another host’s key”. A member clears the binding with Clear host key on Settings → API keys. The next claim binds the new host. See Moving a runner to a new host.

A build fails with agent_credential_missing or disk_limit_exceeded

  • agent_credential_missing: the runner has no Claude credential. Give it one and restart the runner.
  • disk_limit_exceeded: the build outgrew runner.build.disk. Raise it.
See Outcomes and reasons.