Files
personal-agent-os/memory-systems/agent-os/01_Wiki/hermes-cron-delivery-gateway-config.md
T
2026-10-02 18:00:15 -07:00

3.0 KiB

"cannot import name X from Y" in a long-running process after an update

2026-10-02, jimmy. Replaces an earlier note that blamed the gateway for cron-delivery failures. The real cause is stale modules in a process that was already running when the update landed. Status: resolved and understood.

Symptom

cannot import name 'UNAUTHORIZED_DM_BEHAVIORS' from 'gateway.config'
(C:\Users\fight\AppData\Local\hermes\hermes-agent\gateway\config.py)

The cited file defines the name unconditionally at module scope, and its bytecode is current. Fresh processes import it fine; only long-running processes that predate an update fail. Two unrelated modules failed this way in one session:

Failing import Introduced by
UNAUTHORIZED_DM_BEHAVIORS from gateway.config a6e934e0fd feat(gateway): 'decline' unauthorized-DM behavior
tool_errors_since from tools.code_execution_rpc 3e1bfb2935 fix(execute_code): report in-script tool errors

Root cause

A process running before the update holds pre-update module objects in sys.modules. When newly loaded code runs from <stale module> import <name the update added>, the import machinery sees the module already cached, looks for the attribute on the stale object, and raises ImportError: cannot import name …, reporting the module's __file__ — which is correct and unchanged. That is exactly why it reads like a file or bytecode defect and is not one.

Timeline here: session started 08:26:41, update applied 08:45:11, both names arrived with that update. The execute_code tool failing with the same shape in the same session confirmed it independently.

How it was proven

  • Fresh processes import both names, in every interpreter and PYTHONPATH variant tested, in both the pre-PM and PM environments.
  • .pyc headers match their sources and the bytecode contains both names — no stale bytecode.
  • No shadowing gateway/ copy on any sys.path; workspace snapshots are byte-identical to the repo (same sha256).
  • The child environment used by cron delivery (served_profile_child_env + pin_hermes_tree_on_pythonpath) was replicated exactly — it loads fine.
  • Every failing attempt was a manual job fire, which executes in the calling session's process tree. Running the same job through the gateway delivered natively: status: ok, delivery_error: None.

Rule

Restart long-running Hermes processes after an update — the session that ran it, any long-lived agent process, and the gateway. The update receipt warns about this (gateway_restart.incomplete / "mixed sys.modules"); that warning is the real signal, not noise. When you see cannot import name X from Y and Y does define X, check process start time against the last update before reading any source. There is no in-process recovery: the stale object cannot gain the attribute.