Blockers and blocked tasks
What a blocker is, which ones also flip a task to the blocked status and which don't, which clear themselves — including one you can set to release on its own at a future time — and how to file, view, and resolve one from the web app or CLI.
A blocker is a record on a task explaining why it’s paused, or why CodeHerder is holding it back from being picked up. Some blockers also move the task to the reserved blocked status; a dependency blocker doesn’t — the task stays where it was, just held out of the staffing queue. A blocker can also carry a release time, so it clears by itself once that time passes. This page covers all of that: what a blocker is, every way one ends up on a task, which need you and which clear themselves, and how to file, inspect, and resolve one.
What a blocker is
A blocker records who filed it (a person, an agent, or CodeHerder itself), when, an optional link to a related task, and a reason. A task can carry more than one blocker at once.
How a task gets a blocker
These move the task’s status to blocked — it drops out of its current stage until every one of them is resolved:
- Someone files a blocker. A person or an agent records that the task is stuck on something external — optionally with a future release time, covered in Blockers that release themselves below. See Filing a blocker yourself for how.
- An agent asks a question it can’t answer alone. The blocker reads “Waiting for human answer to agent question” until you answer the question — from the Dashboard, the task itself, or wherever else you happen to meet the agent. See When an agent needs your input.
- The review or verify reject loop hit its round cap. See How work flows for how the round cap works and how to change it.
- The task’s spend reached a cost cap. See Spend limits.
- Sessions kept failing to start on the assigned device. See Why isn’t my task moving? for the device-health causes this usually traces back to.
A dependency blocker works differently: the task’s status doesn’t change — it stays wherever it was, normally todo — but CodeHerder still files an active blocker on it and holds it out of the staffing queue until the blocker clears. See Which blockers need you below for the dependency reason and what, if anything, to do about it.
Filing a blocker yourself on a task that has already reached done, cancelled, or archived is rejected outright — a finished task can’t be paused.
Which blockers need you
Reading the reason on a blocker tells you whether it clears itself or needs you to act:
- A reason starting with
unmet dependency:is filed and cleared automatically by CodeHerder — as soon as the named upstream task finishes, the note clears and the task becomes eligible for staffing again. That includes an upstream that’s cancelled or archived instead of completed: the blocker still clears, and CodeHerder separately tells you the upstream’s work never landed (see When an upstream is cancelled or archived). There is nothing for you to do to clear this blocker. - A blocker from an agent question clears the moment you answer it — see When an agent needs your input for every place you can do that.
- A blocker filed with a release time clears itself once that time passes — see Blockers that release themselves below. There is nothing for you to do to clear this blocker either, though you can still resolve it early by hand.
- Any other blocker (one you or a teammate filed with no release time, or one CodeHerder filed for a reject-loop cap or spend cap) needs to be resolved by hand, as described below.
One more thing worth knowing if you’re actively building on a task: adding a dependency to a task that’s already underway pulls it back. CodeHerder retires its live session, discards that session’s in-progress work, and reverts the task to its starting stage so the dependency gate can govern when it’s allowed to proceed again. Add dependencies before work starts where you can, and see Task dependencies for the full dependency model.
Blockers that release themselves
Any blocker you file can carry a release time — a future instant after which CodeHerder resolves it for you. It’s useful for a cool-down period, a wait on a vendor maintenance window, or simply “don’t touch this again until Monday” — anything where you already know when the hold should end, so there’s no reason to make anyone come back and clear it by hand.
Web app: the File blocker panel has an optional Auto-release at field — a date and time picker, read in your own time zone.
CLI: add --until or --for to ch task block, mutually exclusive:
ch task block <taskId> --reason "Waiting out the maintenance window" --until 2026-09-01T09:00:00Z
ch task block <taskId> --reason "Cooling down before retry" --for 24h
--until takes a full timestamp, or a bare date (2026-09-01) — a bare date resolves to midnight UTC, so if you mean a specific time of day in your own zone, spell out the full timestamp instead. --for takes a duration counted from now, in hours and minutes only — there’s no day unit, so write a week as 168h, not 7d. Either way, the release time has to be in the future; CodeHerder rejects the blocker outright if it isn’t.
When the release time arrives, CodeHerder resolves the blocker for you — shortly after that time, not to the second, since it’s checked on a regular pass rather than watched continuously. If the blocker had moved the task to blocked and nothing else is holding it, the task returns to its stage automatically, the same as resolving it by hand would. An auto-released blocker shows no one as the resolver, since you didn’t act on it — just the time it cleared.
Filing a blocker with a release time doesn’t stop you from resolving it early — see Resolving a blocker below. There’s no way to change a release time once it’s set; resolve the blocker and file a new one with the time you actually want.
The Blockers page
The Blockers link stays in the sidebar at all times, with a pulsing red badge showing the count of active blockers across the workspace — the badge is absent when that count is zero.
The page lists every blocker, one row per blocker:
- Task — the affected task; click the row to open it.
- Reason — the blocker’s reason text; a dependency blocker adds a line naming the upstream task it’s waiting on, and a blocker with a release time adds a line showing when it releases.
- Filed by — who or what filed it.
- Created — when it was filed.
- An action column with a Resolve button on active rows. A resolved row shows when it was resolved and by whom instead.
An Active only checkbox above the list is on by default, hiding already-resolved blockers; a row whose own task has since been cancelled carries a task cancelled badge.
Your personal My work view also lists the workspace’s open blockers with the same Resolve action — see My work.
Filing a blocker yourself
Web app: open the task and use the File blocker panel in its detail page. Alongside the reason, the panel has an optional Auto-release at field — see Blockers that release themselves above. To link a specific upstream task to the blocker, use the CLI form below.
CLI:
ch task block <taskId> --reason "Waiting for the vendor API contract" [--blocked-on <upstreamTaskId>] [--until <timestamp>|--for <duration>]
--reason also accepts --from-file <path> or a piped-in body for longer notes. --blocked-on is optional and names a related task; it’s available from the CLI only. --until/--for set a release time and are covered in Blockers that release themselves above. Filing a blocker moves the task straight to blocked from whatever active stage it was in.
Viewing a task’s blockers
ch task blockers <taskId> # every blocker on the task, active and resolved
ch task blockers <taskId> --active # only unresolved blockers
ch task blockers <taskId> --limit N --cursor <token> # default 200, max 500
The table’s Reason column truncates long reasons — for the full reason on an engine-filed note, read the task’s detail page in the web app, or add --json to the command above. A RELEASES-AT column shows the release time for a blocker that has one, in your own time zone, and - for one that doesn’t. See Paging through long lists.
Resolving a blocker
Pass the blocker’s own ID from the list above — not the task ID:
ch task unblock <blockerId>
Any workspace member can resolve a blocker by hand at any time — including one with a release time still ahead of it, resolving it early rather than waiting for that time to pass. For a blocker that moved the task to blocked (see How a task gets a blocker above), resolving the last active one automatically returns the task to the stage it was at when it was blocked, and work resumes with no further action. If CodeHerder can’t determine that stage, the task stays at blocked — move it forward yourself:
ch task status <taskId> <stage>
A dependency blocker doesn’t return a task from blocked, since the task’s status never changed in the first place — see Which blockers need you above for what actually has to happen before the task can proceed.
Related guides
- Why isn’t my task moving? — start here to diagnose a stuck task, including causes beyond blockers
- Collaborating — task dependencies, the model this page’s dependency blockers are built on
- When an agent needs your input — the question-and-answer flow that can also block a task
- How work flows — the reject-loop round cap and stage pipeline
- Spend limits — cost caps that can block a task
- My work — your personal queue of blockers waiting on you
- Review debt — how often the reject-loop cap actually fires across the workspace
