await_human() calls LangGraph’s interrupt(...), which raises and parks the graph. The DRIVER (the code running the graph) catches our shaped interrupt, posts the task to the awaithumans server, polls until terminal, and resumes the graph with the human’s response.
Install
Node side
await_human() is synchronous — matches LangGraph’s node API. The driver loop handles the actual blocking.
Driver side
drive_human_loop:
- Streams the graph forward
- Catches our shaped interrupt (anything with the magic
awaithumanskey) - POSTs the task to the awaithumans server
- Long-polls until terminal
- Resumes the graph with
Command(resume=response) - Returns the graph’s final state
Re-execution semantics
LangGraph re-executes the entire node on resume. Any work BEFOREawait_human(...) runs twice. Move expensive or non-idempotent work to a separate node downstream:
Error contract
The driver maps polling status to typed exceptions:| Status | Exception |
|---|---|
completed | (return validated response to node) |
timed_out | TaskTimeoutError |
cancelled | TaskCancelledError |
verification_exhausted | VerificationExhaustedError |
drive_human_loop to recover.
Why this works under failure
- Driver process dies during the await — LangGraph’s checkpointer (e.g. SQLite, Postgres, Redis) persists graph state. Re-running the script with the same
thread_idresumes from the parked node. The deterministicidempotency_key(default:langgraph:{sha256(task,payload)}) means the awaithumans server returns the existing task. - awaithumans server restarts — tasks are persisted; on restart the dashboard reconnects and the polling driver resumes.
- Human times out —
drive_human_loopraisesTaskTimeoutError. Catch it, retry with a different reviewer, or fail closed.
End-to-end example
Two runnable examples in the repo, same flow in each language:| Example | Language | Entry point |
|---|---|---|
examples/langgraph-py/ | Python | app.py (FastAPI graph host) + kickoff.py |
examples/langgraph-ts/ | TypeScript | app.ts + kickoff.ts |
awaithumans dev. See the per-example README for the run commands.
Cross-language
The TypeScript adapter atawaithumans/langgraph produces the same wire format. A TS driver can resume a graph paused under Python and vice versa.
Common gotchas
- No checkpointer = no interrupts. LangGraph requires a checkpointer to support
interrupt(...). Production graphs should use a durable backend (SQLite / Postgres / Redis), notMemorySaver. - Side effects before
await_human. Run twice on resume. Move them after, or wrap in idempotency. - Multiple
await_humancalls in one node. Each interrupts independently; LangGraph routes resume values by call order. Pass distinctidempotency_key=if the(task, payload)tuples might collide.
Where to next
- Webhooks (
callback_url) — wire format and signature scheme for the callback your driver receives - Testing — patterns for testing graph nodes that call
await_human - Temporal adapter — the same pattern with signals instead of interrupt/resume