# Why isn't my task moving?

Source: https://codeherder.com/docs/task-not-moving/

A diagnostic guide for a task that looks stuck, and how to unstick it.

When a task sits in the same status longer than expected, start here. Most causes leave a visible signal — the quickest first step is always to read what the task is actually showing you:

```
ch task show <taskId>
```

The output surfaces the current status (with a pointer to `ch task blockers` when it’s `blocked`), a `next stages` row, an `advance to <stage>` row naming what’s blocking the next move, any pending-approval banner, whether the task is stalled, and the last touch time. Match what you see to one of the causes below.

## Start with the advance row

Before working through the causes below, check the `advance to <stage>` row `ch task show <taskId>` prints. It’s the fastest way to learn whether a task can move on, and what’s missing when it can’t:

```
advance to review:  blocked — artifact: required field is empty
```

The web app’s task page shows the same verdict below the stage breadcrumb, as an **Advance to `<stage>`** button or, in its place, a line naming what’s outstanding.

Act on what the row names:

- **A field name and reason** (like `artifact: required field is empty` above): fill that field. See **A required field is missing at the current stage**, below.
- **An outstanding checklist**: tick the remaining acceptance-criteria items. See [Write acceptance criteria](https://codeherder.com/docs/writing-tasks/#write-acceptance-criteria).
- **A verification**: record a result for it. See **Verification keeps failing**, below.
- **A sentence about a dependency, unfinished child tasks, or a change-shape limit**: see the matching cause below.

A row like this is never a stuck task — it’s an unmet requirement, and it already names which one. If `ch task show` prints no `advance to` row at all, the task has no next stage right now (terminal, or its pipeline ends here) — expected, not a symptom; look at the causes below instead.

### What the row doesn’t cover

`not blocked` means the task meets the requirements for its next move — it doesn’t mean the task is about to move. The row reports requirement checks only, the ones in **Before a task moves on** in [How work flows](https://codeherder.com/docs/how-work-flows/).

So a task can read `not blocked` and still go nowhere: a waiting approval, no available device, a signed-out host CLI, a stalled assignee, a spend cap, the reject-loop cap — none show up in the row. Each has its own section below.

The fastest way to find which one applies: let the activity feed name it directly — see **Let the activity feed name the cause**, next.

## Let the activity feed name the cause

If the advance row says nothing is outstanding and the task still isn’t moving, don’t stop there — the activity feed usually names the cause in a plain sentence:

```
ch activity task <taskId> --type task.staffing_skipped,task.start_refused
```

The `WHY` column carries that sentence for a staffing-skip or start-refusal row — empty when no reason was recorded, the raw value when your `ch` doesn’t yet know it. The `--type` filter cuts the per-turn cost rows this feed otherwise mixes in. See [Activity feeds](https://codeherder.com/docs/activity/) for the full column reference.

In the web app, the task page’s **Activity** section shows the same sentence as a row’s detail, but not always by default: a staffing-skip sentence is on the curated story; a start-refusal sentence needs the **All events** toggle in the section’s header. The CLI shows both with no toggle — check it, or flip the toggle, before assuming nothing was recorded.

A few sentences name a read that failed for this sweep only (a count, a config, the task row) or a session create that didn’t take. These retry automatically; act only if one keeps recurring for the same task.

## 1. Waiting on an approval

**Signal:** `ch task show <taskId>` shows a `⏸ pending advance → <stage>` banner. The task’s status has not changed, but an advance request is waiting.

**What happened:** CodeHerder tried to advance the task and hit an approval gate — a rule requiring a designated member to sign off first. The request is recorded but held until an eligible approver acts. This only happens on a stage CodeHerder can leave automatically — for the built-in feature, bug, and story types, that’s the `todo` stage a task starts at.

**What to do:** If you are an eligible approver, approve it:

```
ch task approve <taskId>
```

To see all pending advances waiting on you across the workspace:

```
ch task list --awaiting-approval
```

If you are not the right approver, share the task with whoever is. If the work shouldn’t start yet, reject it instead:

```
ch task reject <taskId>
```

For how approval gates are configured and all the approval actions, see [Approvals & staying in control](https://codeherder.com/docs/approvals/).

## 2. Blocked

A task moves to the `blocked` status several ways, including someone filing an explicit blocker, an agent posting a question it couldn’t answer alone, the task hitting the automatic reject-loop cap, or its spend reaching a cost cap — see [How a task gets a blocker](https://codeherder.com/docs/blockers/#how-a-task-gets-a-blocker) for the full list. An unmet dependency works differently and does **not** change the task’s status — see *Unresolved dependency* below.

### Filed blocker

**Signal:** `ch task show <taskId>` shows status `blocked` (with a pointer to `ch task blockers`).

**What to do:** List the active blockers, then check the RELEASES-AT column — a blocker with a release time clears itself once that time passes, so there’s nothing to do but wait. For anything else, resolve it by hand:

```
ch task blockers <taskId> --active
ch task unblock <blockerId>
```

Pass the **blocker ID** (from the blockers list), not the task ID. Resolving the last active blocker returns the task automatically to the stage it was at.

If it stays at `blocked` anyway, move it back manually — `ch task show <taskId>` ’s `next stages` row names the stage:

```
ch task status <taskId> <stage>
```

For the full blocker lifecycle, see [Blockers and blocked tasks](https://codeherder.com/docs/blockers/).

### Agent question

**Signal:** The task is `blocked` with a reason of “Waiting for human answer to agent question”, shown as an open card on the Dashboard and on the task’s own page.

**What to do:** Answer it from wherever you’re looking at it — see [When an agent needs your input](https://codeherder.com/docs/agent-input/) for the full list. Submitting clears the blocker and resumes the task automatically; nothing to unblock by hand.

### Unresolved dependency

**Signal:** The task sits at `todo` and never advances, even though nothing else looks wrong — its status is **not** `blocked`. `ch task blockers <taskId> --active` shows a blocker whose reason starts with `unmet dependency:`; that blocker, not the status, is holding it back. Check upstream tasks:

```
ch task deps <taskId>
```

The Tasks page’s [Graph view](https://codeherder.com/docs/tracking-work/#graph-view) lays out the whole upstream chain at once.

**What to do:** Every upstream must finish before this task can advance — planning stages included. If one is still in progress, no action is needed: once all finish, the blocker clears automatically. If an upstream won’t finish on its own, cancelling it (with a reason) or removing the dependency edge both clear the way, since cancelling counts as finishing. Cancel this task too if it no longer makes sense without that result.

See [Collaborating](https://codeherder.com/docs/collaborating/) and [Blockers and blocked tasks](https://codeherder.com/docs/blockers/). The `advance to <stage>` row above already names an unmet dependency.

### Reject-loop cap reached

**Signal:** The task was sent back into `code` from `review`, `verify`, or `merge` enough times to hit the round cap on one of those routes. The engine moves it to `blocked` automatically and posts a blocker note explaining what happened.

**What to do:** A human needs to review this. Read the blocker note with `ch task blockers <taskId>` (`ch task show <taskId>` only points at that command), then fix the blocker and resume the task at the stage that fits, or cancel it:

```
ch task status <taskId> <stage>           # resume
ch task status <taskId> cancelled --reason "Superseded by …"  # cancel
```

For how the reject-loop cap works and how to change it per workflow, see [How work flows](https://codeherder.com/docs/how-work-flows/).

### Verification keeps failing

**Signal:** The task leaves `code`, reaches `review` or `verify`, then returns to `code` on its own — possibly more than once — with no manual send-back.

**What happened:** the stage’s agent recorded a `fail` for a required verification, so CodeHerder routed the task back to `code` for another build attempt — the same reject loop as above, just triggered by a recorded result rather than a human reviewer. See the **Stage verifications** section of [How work flows](https://codeherder.com/docs/how-work-flows/). If a task is simply sitting with no `pass` recorded yet, rather than bouncing, the `advance to <stage>` row in **Start with the advance row** above names the missing verification.

**What to do:** Usually nothing — the workflow is self-correcting. Read the hand-off comment on the task to see what failed:

```
ch task show <taskId>
```

To see exactly which check failed, list every verification result recorded for the task:

```
ch task verifications <taskId>
```

The output lists stage, check, status, build attempt, and any recorded detail, most recent first. `<status>` is one of `pass`, `fail`, `error`, or `skipped`. Omit `<taskId>` and it defaults to `CH_TASK_ID`.

Each return to `code` this way escalates to a more capable model for the next attempt — see **Automatic model escalation on rework** in [How work flows](https://codeherder.com/docs/how-work-flows/). If it keeps bouncing, it eventually hits the round cap and moves to `blocked` — follow **Reject-loop cap reached** above.

### A result is refused

**Signal:** `ch task verify` is refused with a message that a required verification pass may not come from the party it checks, and the `advance to <stage>` row still names the verification.

**What happened:** CodeHerder keeps the checker apart from the author on two kinds of check. It refuses a required `pass` on the `verify` goal gate from an author, unless that member worked the `verify` stage itself. It refuses a judged check from anyone who worked that stage, or who already recorded a result on this attempt. A test, lint, or typecheck check has no such rule. See **Who can record a pass** in [How work flows](https://codeherder.com/docs/how-work-flows/).

**What to do:** Pick the case that fits:

- For the `verify` goal gate, have a person or agent who did not build the change record the `pass`. For example, a workspace member who did not work on the task runs `ch task verify <taskId> goal_gate pass`.
- For a judged check, have someone who did not work that stage record the result.

The refusal message may suggest `--execute`. That helps only when the check’s stage declares a command. The built-in goal gate declares none.

### Spend cap reached

**Signal:** `ch task blockers <taskId> --active` shows a blocker whose reason starts with `cost budget exceeded (task)` or `cost budget exceeded (workspace)`.

**What happened:** Either the task’s own spend, or its workspace’s, reached a cost cap, so CodeHerder stopped its active sessions and moved it to `blocked`. Those sessions end **Released**, not failed — see [Sandbox state vs. a run’s outcome](https://codeherder.com/docs/following-a-live-session/#sandbox-state-vs-a-runs-outcome).

**What to do:** Read the word in parentheses before raising anything — it names which cap applied; raising the wrong one leaves the task blocked. See **Reading the blocker** in [Spend limits](https://codeherder.com/docs/spend-limits/). Once raised or cleared, resolve the blocker:

```
ch task blockers <taskId> --active
ch task unblock <blockerId>
```

## 3. Stalled assignee

**Signal:** `ch task show <taskId>` shows a `stalled:` row and a `last touch:` row.

**What happened:** Whoever — or whatever — is working the current stage hasn’t touched it for long enough that CodeHerder flagged it as stalled. The stage is still being worked; it’s just not progressing.

Looking across the whole workspace? Run `ch task list --stalled`, or turn on the **Stalled** filter on the Tasks page.

**What to do:** Touch is the working agent’s own liveness ping — not something you run as a human; doing so returns an error. Instead:

- Comment on the task — posting is itself confirmed activity and clears the flag right away.
- Move it to its next stage, if it’s ready to advance.
- Pinning a different assignee from the **Assignee** dropdown doesn’t clear a stall by itself, and on a built-in type doesn’t even change which agent runs it next — see **The Assignee dropdown — what it does today** in [Assigning and claiming work](https://codeherder.com/docs/assigning-work/) first.

How long a task can sit untouched before it’s flagged is a per-workspace setting (60 minutes by default) — see **Editing a workspace** in [Managing workspaces](https://codeherder.com/docs/workspaces/). For the full stall flow, see [Assigning and claiming work](https://codeherder.com/docs/assigning-work/).

## 4. No agent or device available to staff the task

An eligible, ready task can still sit in `todo` if the staffing prerequisites are not met. Check the following. To spot a coverage gap for your whole workspace before it strands the next task, see [Staffing coverage](https://codeherder.com/docs/stage-staffing/).

### Account is at its concurrent-session limit

**Signal:** The activity feed’s cause names the account as already at its session limit.

**What happened:** Your plan caps how many agent sessions run across the whole account at once, regardless of how many devices you have free.

**What to do:** `ch workspace plan` shows the `concurrent agents` line against the limit. Wait for a slot to free, or raise the plan’s concurrency limit — see [Plans and limits](https://codeherder.com/docs/plans-and-limits/). This differs from **Device at capacity**, below, which caps one device, not the account.

### A session is already live for this task

**Signal:** The activity feed’s cause names an already-live session for this task.

**What happened:** Only one session runs a task at a time. CodeHerder re-checks this on every staffing sweep, so seeing it once for a task that’s genuinely being worked is expected, not a symptom.

**What to do:** Open the task’s Sessions view to confirm a session is running. If the task also looks stalled (see **Stalled assignee** above) and you’re confident the session is dead, stop it directly, then a fresh one can start:

```
ch session stop <sessionId>
```

See [Following a live agent session](https://codeherder.com/docs/following-a-live-session/) for telling a running session from one gone quiet.

### No repository resolves to build in

**Signal:** The task’s status is `blocked`, and `ch task blockers <taskId>` shows a note naming one or more candidate repositories.

**What happened:** CodeHerder resolves what a task builds in automatically — the task’s own attached repo set first, then a workspace default, then the one active repository across the workspace and its parent groups — and blocks the task right away when none of those resolve. This most often happens when a workspace has more than one active repository and neither a task-level repo set nor a workspace default names the one to use; the blocker note lists every repo it found in play. See [Tasks that span several repositories](https://codeherder.com/docs/multi-repo-tasks/) and **Which repositories a task builds in** in [Connecting repositories](https://codeherder.com/docs/repositories/) for the full resolution order.

**What to do:** Either attach a repo set to the task —

```
ch task repos create <taskId> <repo>
```

— or set a default for the workspace so future tasks resolve without you having to name one each time:

```
ch workspace edit <workspace> --default-repo <name|id>
```

This also works if the repository is inherited from a parent group. If you’d rather not set a default, keep exactly one active repository across the workspace and its parent groups, and archive the rest with `ch repo archive <name>`. Take care with a repo that belongs to a parent group: archiving it removes it from every workspace under that group. Set a workspace default instead when the extra repo is inherited.

A task blocked here has no repo attached, so either fix works: a new default or an attached repo applies the next time CodeHerder starts a session for the task. A task that already got a repo when it was created keeps that repo. A later change to the default doesn’t move it.

A similar note appears when a repository has been **archived**: the task’s set, or the workspace default, names a repo that’s no longer active, and CodeHerder won’t start a session for the task. The note names the repo. Bring it back with `ch repo restore <name>`, remove it from the task with `ch task repos delete <taskId> <repo>`, or change the workspace default with `ch workspace edit <workspace> --default-repo <name|id>` (or `--clear-default-repo`).

A related problem lands a task in the same `blocked` state: **more than ten repos attached** — a sandbox can only provision ten at once, so declaring more blocks the task with a note listing them all. Trim with `ch task repos delete <taskId> <repo>`.

A repo that fails to clone is different, not an immediate block — the sandbox fails to provision, the failure shows in the task’s Sessions view, and CodeHerder retries automatically before giving up. In a multi-repo set, the whole sandbox is abandoned and the failure names the culprit repo; a sibling that had already cloned is reused, not re-cloned. Fix whatever the clone error names — usually credentials or a URL, see **Git access to clone the repository** in [Connecting repositories](https://codeherder.com/docs/repositories/) — and the next retry picks it up.

### Device is Offline

**Signal:** The task is unassigned and waiting; your devices are Offline.

```
ch device list
```

**What to do:** Start or restart `ch device-server` on the machine — the device comes Online as soon as it connects. No device registered yet? See [How do I add a device?](https://codeherder.com/docs/adding-a-device/). Or set up a [MicroVM runner](https://codeherder.com/docs/microvm-runners/) so CodeHerder launches one on its own next time.

### Agent not eligible on any linked device

**Signal:** The device is Online but not picking up work for this agent.

An agent is eligible on any device linked to its workspace (or an ancestor group) whose capabilities the device covers — no separate assignment step. Check what the engine sees:

```
ch task placement <taskId> --agent <agentId>
ch agent placement <agentId>   # the agent alone, regardless of task
```

Either lists every candidate device and, for each ineligible one, the exact refusal reason. The report doesn’t check device health; see [Unhealthy device](https://codeherder.com/docs/task-not-moving/#unhealthy-device) and [The Placement report](https://codeherder.com/docs/placement/).

### Secret reference unacknowledged or unresolvable

**Signal:** The device is Online and the agent is eligible on it, but a session for this agent still won’t start. The task shows that the agent failed to start, and the reason mentions a secret reference or a secret variable the device owner hasn’t acknowledged, or a secret reference that couldn’t be resolved on the device.

**What happened:** This is the same acknowledgement gate for two things. A launch config’s device-side credential (an `@secret:<key>` value) must be explicitly acknowledged by the device or a workspace owner, with the value actually stored there. A [secret variable](https://codeherder.com/docs/variables/) at group, workspace, device, or agent scope needs the same acknowledgement, even though it isn’t an `@secret:` reference. Either one missing refuses the session to start — including after adding a new key to an already-acknowledged config.

**What to do:** Set the device’s acknowledged-secrets list to include every referenced key, `@secret:` or secret-variable alike:

```
ch device secrets set <deviceId> --secret <key>
```

This is a whole-set replace, not a diff — pass every key in scope, not just the new one. If the failure says a reference couldn’t be resolved instead, it won’t name the key — check which `@secret:<key>` references the launch config uses and confirm each is stored on that device. See [Secrets on a device](https://codeherder.com/docs/device-secrets/) and [Variables](https://codeherder.com/docs/variables/).

### Device at capacity

**Signal:** The device is Online and eligible agents exist for it, but the device is fully busy.

Check the **Capacity** panel under **Set up → Devices → [the device]** for its **Max concurrent sessions** limit — see [Concurrency and capacity](https://codeherder.com/docs/devices/#concurrency-and-capacity). At the limit, new tasks queue and start as a running session finishes; raise the limit in the same panel for more parallel work.

If the device stays full with nothing seeming to run, some worktree slots may be left over from ended sessions. Expand the row and check **Worktree slots** in the Health snapshot — if it reports reclaimable slots, force an immediate clean-up:

```
ch device reclaim <deviceId>
```

See **Reclaiming stuck worktree slots** in [Managing your devices](https://codeherder.com/docs/devices/#reclaiming-stuck-worktree-slots) for the full picture, including who can run it and what to check before you do.

### Unhealthy device

**Signal:** The device is Online and has free slots, but a task still does not start. In **Set up → Devices**, the device row shows an amber **Not staffable** badge next to the health badge. If a session was attempted and failed, a task comment says the device is unhealthy and names the failing check.

**What happened:** CodeHerder runs readiness checks on every device and withholds new sessions from any device whose health rolls up to **unhealthy** — at least one blocking check failed. Common causes: expiring git credentials, an unwritable session-root directory, critically low disk space, or a coding CLI installed but not signed in (see [Coding agent not signed in](https://codeherder.com/docs/task-not-moving/#coding-agent-not-signed-in) below).

**What to do:**

1. In **Set up → Devices**, click the device row to expand it.
2. The **Health snapshot** panel lists every failing check with a summary and remediation tip. If you only see a notice, ask the device’s owner or an admin to read it — see [Who sees host details](https://codeherder.com/docs/devices/#who-sees-host-details).
3. Fix it on the device — for example, re-run `gh auth setup-git`, or free disk space.
4. The device re-checks within roughly five minutes, or immediately on restarting `ch device-server`. Once it passes, the **Not staffable** badge disappears.

For every readiness check, see **Device health and readiness checks** in [Managing your devices](https://codeherder.com/docs/devices/).

### Coding agent not signed in

**Signal:** The device is Online but a task assigned to it won’t start. In **Set up → Devices**, the device row shows an amber **Not staffable** badge next to the health badge. Expanding the row, the **Health snapshot** panel shows the **Harness auth** check failing. If a session was attempted anyway, it dies at startup unable to reach a model.

**What happened:** A coding CLI can be installed on a device but not signed in to its AI provider — installing it only satisfies the **Harness ready** check. A separate **Harness auth** check tests each installed CLI, and blocks work that needs a CLI that is signed out. This is the most common cause of an unhealthy, non-staffable device right after you register it.

**What to do:**

1. On the device, sign in with that harness’s own login command — `claude login`, `codex login`, `cursor-agent login` (or set `CURSOR_API_KEY`), or `opencode auth login`, matching the CLI the agent’s config uses. Pi has no login command: it signs in via a provider API key (for example `ANTHROPIC_API_KEY`) in the device’s environment, or a credential file already signed in elsewhere. See [Choosing the coding-agent CLI your agents run](https://codeherder.com/docs/harnesses/).
  - If the failing harness is installed for reasons unrelated to CodeHerder, exclude it instead of signing in: restart `ch device-server` with `--harness-exclude <name>` (or `CH_DS_HARNESS_EXCLUDE=<name>`). It then no longer counts against **Harness ready** or **Harness auth** — but keep at least one harness on the device, or **Harness ready** fails instead.
2. The device re-checks automatically within roughly five minutes, or immediately if you restart `ch device-server`.
3. Once the check passes, the **Not staffable** badge disappears and the engine resumes placing work on the device.

For the full readiness-check reference, see **Device health and readiness checks** in [Managing your devices](https://codeherder.com/docs/devices/#device-health-and-readiness-checks).

### Device is drained (paused)

**Signal:** The device is Online, healthy, and not at capacity, but tasks still are not placed there. `ch device list` shows `drained` in the **STATE** column, or `ch device show <deviceId>` prints `drained: yes (since <when>)`.

**What happened:** Someone paused the device — **Disable** in the web app or `ch device disable <deviceId>` — to stop new work landing there without interrupting running sessions. Online state and health badge don’t change; only new placement is skipped.

**What to do:** Resume placement:

```
ch device enable <deviceId>
```

Or click **Enable** on the device’s row in **Set up → Devices**. Placement resumes immediately.

For the full pause/resume workflow, see **Pausing a device** in [Managing your devices](https://codeherder.com/docs/devices/).

### Claude usage or spend limit exhausted

**Signal:** The device is Online, healthy, not at capacity, and has an eligible agent — yet tasks still do not start. In **Set up → Devices**, the device carries a **Claude exhausted** badge next to its health badge (or **Claude subscription exhausted** on its detail page). `ch device show <deviceId>` prints the same thing as a `staffing` row.

**What happened:** CodeHerder monitors each device’s Claude usage — a subscription plan’s usage pools, or an enterprise/organization credential’s spend caps. When a meter reaches 100% before it resets, the engine stops routing new tasks there to avoid stranding sessions on a credential that will reject them. Running sessions aren’t interrupted, and the device’s Online/healthy status doesn’t change — its health checks don’t reflect this.

**What to do:** No manual action is required. Once the reset time shown on the meter passes, the engine resumes placing tasks automatically. The **resets in** text shows how long to wait.

If the stage’s **Assignee** is **Auto — best fit** and a second device with headroom is registered, CodeHerder already tries it — no action needed. Otherwise, the fix is a second Online, healthy device with a capable agent, or simply waiting for the window to roll over.

For the full explanation — why the pause is device-wide, why switching launch configs on the same device doesn’t help, and how failover across devices actually works — see [AI usage limits](https://codeherder.com/docs/ai-usage-limits/).

### In a backoff window after a recent failure

**Signal:** The activity feed’s cause names a backoff window after a prior failure.

**What happened:** After a session fails to start, CodeHerder waits out a short backoff window instead of retrying in a tight loop.

**What to do:** Nothing — it retries automatically once the window ends. If it keeps re-entering backoff, check the task’s Sessions view for what the failed attempt reported and follow the matching cause on this page.

### Capability mismatch

**Signal:** The device is Online and has free slots, but the task still does not start.

See [Capabilities](https://codeherder.com/docs/capabilities/) for what a capability label is and the different rules for each place it appears.

There are three distinct causes that produce this signal:

**No agent’s launch config covers what the stage requires.** Workflow stages declare capabilities a launch config must carry — for example, a planning stage may require `model:opus`. Set these with `--cap` in `ch agent config edit`, or `--config-cap` at agent-creation time (see [Creating a runnable agent in one call](https://codeherder.com/docs/agents-and-cli/#creating-a-runnable-agent-in-one-call)). If no launch config in your workspace carries the needed capability, the stage is unstaffable.

The web app surfaces this as a **Capability gap** callout at the top of **Set up → Agents**, naming what’s missing:

```
ch agent config edit <agentId> --cap <existing-cap> --cap <missing-cap>
```

`--cap` replaces the whole capability list, so name every existing one plus the missing one. `config edit` leaves other fields — command, args, environment — as they are. For a brand-new agent, create it and its launch config together: `ch agent create --harness <h> --config-cap <capability>`. See [Agents and the CLI](https://codeherder.com/docs/agents-and-cli/).

**The task’s own required capabilities are no longer covered.** A task can carry its own `--cap` requirements, combined with each stage’s as the task moves through the workflow — see [Capabilities](https://codeherder.com/docs/capabilities/). A task that passed the create-time check can still stall later once a stage pushes the combined set past what any launch config covers.

This shows a **Start refused** badge (task list and detail) and a **Start refusal** row naming the reason; `ch task show <taskId>` and `--json` ’s `startRefusal` carry the same. Fix with `ch task edit <taskId> --cap <label>...` or `--clear-caps`.

**The agent needs tooling the device lacks.** An agent can require host tools — the AI coding binary or the git-host CLI (`gh` / `glab`) — which devices detect automatically. Install the missing tool; the device picks it up on its next connection. See [Managing your devices](https://codeherder.com/docs/devices/).

### Current stage doesn’t run an agent

**Signal:** The activity feed’s cause names the current stage as one that doesn’t run an agent.

**What happened:** A pipeline’s start and end stages, like `todo` and `done`, are bridge stages that never spawn an agent by design — see [Staffing coverage](https://codeherder.com/docs/stage-staffing/). An epic or initiative reports this for its own container stage the whole time children run, which needs no action. Otherwise, on a story, feature, bug, or `task` build stage, it usually means that stage’s workflow has no real agent stage attached.

**What to do:** For a bridge or container stage, do nothing — it’s expected. Otherwise, the workflow’s stage list needs a real agent stage there; see [How work flows](https://codeherder.com/docs/how-work-flows/).

## 5. Stuck at `code` or `merge` — host-CLI not authenticated

**Signal:** The task is stuck at the `code` or `merge` stage. Looking at the task’s Sessions view shows a failed session with an error about the git-host CLI (`gh` or `glab`) not being found or not authenticated.

**What happened:** Agents open and land pull requests through the git-host CLI installed on the device. If it’s missing or its token expired, whichever stage runs first — `code` opening the request, or `merge` landing it — fails and posts the error as a task comment.

**What to do:** On the device that ran the failed session, confirm which CLI is involved and its current state:

```
ch githost auth-check
```

The command detects the host from the repository’s `origin` remote and prints a clear result — `✓` when authenticated, `✗` when not. For a specific repository, supply its remote URL:

```
ch githost auth-check --remote https://github.com/acme/my-app
```

Note: `ch githost auth-check` supports **github.com** and **gitlab.com** only. If your repository is on Bitbucket or a self-hosted instance, the command does not apply — check the host CLI’s own auth status directly.

If the check fails, follow the printed remediation:

- **GitHub**: `gh auth login` on the device.
- **GitLab**: `glab auth login` on the device.

Once the CLI is authenticated, move the task back to the failed stage:

```
ch task status <taskId> code    # if it failed at code
ch task status <taskId> merge   # if it failed at merge
```

The engine staffs the task to the next available session. For the full device requirements — git credentials and host-CLI setup — see [Connecting repositories](https://codeherder.com/docs/repositories/).

## 6. Auto-staffing is off at the entry stage

A task can sit at its workflow’s very first stage, untouched, because auto-staffing is off for that task. One setting controls it, `auto_staff_unassigned`. Start by reading what applies to this task:

```
ch workflow show <taskId>
```

That prints the workflow this task actually runs on. Look for `auto_staff_unassigned`:

- **`true`** — auto-staffing is on for this task. Check the other causes on this page instead.
- **Absent, or `null`** — auto-staffing is off (a missing key and `null` mean the same thing). Keep reading.

When it’s off, the setting comes from the task’s own workflow override if it has one, or its type’s shared settings otherwise. Read the type’s shared settings next — `--json` is required, since the plain-text output omits this setting, and it lists every type, so select `<type>` ’s entry yourself (`--type <type>` narrows plain text only, not `--json`):

```
ch workspace workflow show --json | jq -r '.data[] | select(.typeKey == "<type>") | .schema.auto_staff_unassigned'
```

Without `jq`, find `<type>` ’s entry in `ch workspace workflow show --json` by hand and read `auto_staff_unassigned` inside its `schema` object — don’t read the first one you see, it may belong to a different type.

Compare the two readings:

- **Both are absent or `null`** — the type itself doesn’t auto-staff. See **Auto-staffing is off for the whole type**, below.
- **The type reads `true`, but the task reads absent or `null`** — this task carries its own workflow override. See **A task held back by its own workflow override**, below.

### Auto-staffing is off for the whole type

**Signal:** Both readings above are absent or `null`.

**What happened:** This type’s entry stage leaves newly created, unassigned tasks alone, often deliberately, so a human triages or assigns each one first. On the built-in `task` type it can instead mean this workspace holds an out-of-date copy — each workspace gets its own copy of a type at creation, and one created before a later fix keeps the old setting until a server upgrade repairs it. Only suspect that when nobody turned the setting off on purpose.

**What to do:** Move the task on by hand:

```
ch task status <taskId> <stage>
```

To change it for future tasks of this type, a workspace admin or owner uploads a corrected schema with `auto_staff_unassigned` set to `true`:

```
ch workspace workflow edit <type> --from-file schema.json
```

If the type is the built-in `task` and nobody turned the setting off, upgrade the server instead. The upgrade repairs every stored `task` row automatically. See [Assigning and claiming work](https://codeherder.com/docs/assigning-work/).

### A task held back by its own workflow override

**Signal:** The type reads `true`, but the task reads absent or `null`.

**What happened:** A task can carry its own workflow override — a private copy that keeps the auto-staffing setting as it stood when made, and doesn’t follow later changes to the type. An override made while the type was off keeps this task off even after someone turns the type back on.

**What to do:** Move the task on by hand with `ch task status <taskId> <stage>`, or clear the override so the task follows its type again:

```
ch workflow override clear <taskId>
```

Clearing it reverts the task to the type’s current settings. Workspace admins and owners can run it. If the task sits at a stage the type’s workflow doesn’t have, the command stops and tells you to re-run it with `--migrate <oldStage>=<newStage>`. See [Assigning and claiming work](https://codeherder.com/docs/assigning-work/).

## 7. A container task waiting on its children

**Signal:** The task type is `epic` or `initiative`, and the status is `in_progress`.

**What happened:** `in_progress` is the normal holding state for containers — it means “child tasks are being worked”. The container itself never runs a build stage; it stays at `in_progress` until every child task reaches `done`.

**What to do:** Check the state of the child tasks:

```
ch task children <taskId>
```

Progress the children through their own workflows. The container advances to `done` automatically once all children are complete. You cannot close the container manually until then.

For the container lifecycle and the `planning` stage that precedes `in_progress`, see [How work flows](https://codeherder.com/docs/how-work-flows/) and [Core concepts](https://codeherder.com/docs/concepts/). The `advance to <stage>` row in **Start with the advance row** above names the open child count too, once the container is otherwise ready to close.

## 8. A reopened task that never restarts

**Signal:** The task’s status is `in_progress`, its type is `story`, `feature`, `bug`, or `task` — but no session ever starts. There’s no blocker filed and nothing waiting on you; the task simply sits there looking active.

**What happened:** This task was left at `in_progress` before CodeHerder started refusing that move for these four types. `in_progress` is the correct holding stage for an initiative or epic, but it was never a stage in the story, feature, bug, or task pipeline, so nothing gets staffed there.

**What to do:** Reopen it straight into the stage you actually want:

```
ch task reopen <taskId> code --reason "picking this back up"
```

Or, from the web app, open the task and use the reopen control under its **More** menu. See **Reopening a cancelled or completed task** in [Editing and cancelling tasks](https://codeherder.com/docs/editing-tasks/) for the full target set.

## 9. Refused by a change-shape limit

**Signal:** Advancing the task fails with an error naming a counter and a threshold, for example `advancing out of stage "code" is blocked by its change-shape gate: max_files_changed is 34, exceeding the threshold of 20`.

**What happened:** The stage you’re leaving has an optional limit on how big or spread out the task’s change can be, and the task’s recorded change shape exceeds it. This only happens on a stage where someone has deliberately set such a limit — most workspaces never see this message at all.

**What to do:** Either split the change into smaller tasks so each one fits under the limit, or — if the limit is too tight for this kind of work — raise or remove it on that stage. See [Change shape](https://codeherder.com/docs/change-shape/) for what the counters measure and how to adjust the limit. The `advance to <stage>` row in **Start with the advance row** above shows this same refusal before you even attempt the move.

## 10. A required field is missing at the current stage

**Signal:** Advancing the task fails with an error listing one or more field keys as still needed, the task page shows a note naming which required fields are missing instead of an **Advance to `<stage>`** button, or `ch task show <taskId>` ’s `advance to <stage>` row names the field.

**What happened:** Every stage has a gate — a check that its required fields are filled — and one is still empty. On the built-in `story` / `feature` workflow this most often means the task is at `plan`: it can’t leave until both `acceptance` and `plan` are set, from different sources. You write `acceptance` when filing the task — see [Write acceptance criteria](https://codeherder.com/docs/writing-tasks/#write-acceptance-criteria) — and the planning agent reads it, then writes `plan` in return.

**What to do:** Run `ch task fields <taskId>` to see exactly what this task’s type requires and which fields are still unset:

```
ch task fields <taskId>
```

If `acceptance` is what’s missing, you need to write it yourself — no agent will:

```
ch task edit <taskId> --acceptance-file criteria.md
```

If `plan` is what’s missing and the planning agent is still working, give the session time to finish — it doesn’t appear until the agent saves it. If the field is genuinely stuck empty, fill it yourself:

```
ch task edit <taskId> --plan-file plan.md
```

See **Planning stages and stage gates** in [How work flows](https://codeherder.com/docs/how-work-flows/) for the full gate table, and [Writing tasks an agent can build](https://codeherder.com/docs/writing-tasks/) for what each field holds.

## Related guides

- [When a session goes quiet](https://codeherder.com/docs/following-a-live-session/#when-a-session-goes-quiet) — recovers on its own, not a cause of a stuck task
- [The Placement report](https://codeherder.com/docs/placement/) — the full picture of device eligibility
- [Blockers and blocked tasks](https://codeherder.com/docs/blockers/) — the full blocker lifecycle
- [Tasks that span several repositories](https://codeherder.com/docs/multi-repo-tasks/) — the repo set model
- [How work flows](https://codeherder.com/docs/how-work-flows/) — the stages every task moves through
- [Writing tasks an agent can build](https://codeherder.com/docs/writing-tasks/) — how to file work agents can pick up
- [Assigning and claiming work](https://codeherder.com/docs/assigning-work/) — who ends up working a task
- [Approvals & staying in control](https://codeherder.com/docs/approvals/) — gate advances behind a designated approver
- [Reviewing an agent’s work](https://codeherder.com/docs/reviewing-work/) — the human side of the review stage
- [How do I add a device?](https://codeherder.com/docs/adding-a-device/) — connect a machine if none is registered yet
- [Secrets on a device](https://codeherder.com/docs/device-secrets/) — device-side credentials and their acknowledgement
- [Variables](https://codeherder.com/docs/variables/) — environment variables and secrets at every scope
- [Change shape](https://codeherder.com/docs/change-shape/) — the size/spread limit that can refuse a stage advance
- [Reading the task flow report](https://codeherder.com/docs/task-flow/) — the same wait states and refusals, aggregated across the whole workspace
