NousResearch/hermes-agent · error · TimeoutError
Timed out waiting for the TERMINAL_CWD {'write' if _holds_cw
Error message
Timed out waiting for the TERMINAL_CWD {'write' if _holds_cwd_write else 'read'} lock after {_cwd_lock_timeout:.0f}s — another cron job (a workdir writer, or long-running readers) has held it for longer than the cron inactivity limit. If a workdir job is the holder, stagger its schedule or remove its workdir to unblock this job (#79768). What it means
Error "Timed out waiting for the TERMINAL_CWD {'write' if _holds_cwd_write else 'read'} lock after {_cwd_lock_timeout:.0f}s — another cron job (a workdir writer, or long-running readers) has held it for longer than the cron inactivity limit. If a workdir job is the holder, stagger its schedule or remove its workdir to unblock this job (#79768)." thrown in NousResearch/hermes-agent.
Source
Thrown at cron/scheduler.py:3878
_cwd_lock_acquired = False
# Everything after the acquire MUST live inside this try, so the finally
# below always releases the lock even if the env override or any later
# statement raises. A leaked writer would deadlock the whole scheduler
# (every future job blocks on acquire_*); a leaked reader blocks all
# future writers. Acquire itself can't leak (it either blocks or returns).
_cron_session_var = _VAR_MAP["HERMES_CRON_SESSION"]
_cron_session_token = None
_non_dispatcher_token = None
try:
if not _cwd_lock_acquired:
# Fail closed (#79768): running without the lock would let a
# concurrent workdir job's process-global TERMINAL_CWD override
# leak into this job's shell/file/code-exec commands — silent
# wrong-directory execution, the exact corruption the lock
# exists to prevent. A loud failure is recoverable (next tick /
# manual rerun); a job that ran in the wrong directory is not.
raise TimeoutError(
f"Timed out waiting for the TERMINAL_CWD "
f"{'write' if _holds_cwd_write else 'read'} lock after "
f"{_cwd_lock_timeout:.0f}s — another cron job (a workdir "
f"writer, or long-running readers) has held it for longer "
f"than the cron inactivity limit. If a workdir job is the "
f"holder, stagger its schedule or remove its workdir to "
f"unblock this job (#79768)."
)
# Scope cron approval policy to this job. Keep the token so the finally
# restores the pre-job state instead of pinning an explicit empty value,
# which would suppress the legacy os.environ fallback used by standalone
# cron entrypoints and tests.
_cron_session_token = _cron_session_var.set("1")
# Mark this job as NOT the dispatcher-owned kanban worker.
#
# A kanban worker is a normal `hermes chat -q` CLI agent whose default
# toolset includes `cronjob`, running with HERMES_KANBAN_TASKView on GitHub (pinned to c896c09c42)
Solutions
- Stagger schedules of workdir jobs so they do not hold the TERMINAL_CWD lock past the inactivity limit.
- Remove the workdir from the offending job, or shorten long-running jobs sharing the cwd lock.
When it happens
Trigger: Thrown at cron/scheduler.py:3878 when the library encounters an invalid state.
Common situations: See trigger scenarios.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/b7de65e814d68896.
Report an issue: GitHub.