Summary
active-memory can self-block when it runs its recall subagent from the before_prompt_build hook. The recall call currently does not pass an explicit lane to runEmbeddedPiAgent, so the embedded runner defaults to the global main lane. When the hook itself is already executing as part of a user-visible agent turn on main, the recall subagent is queued behind the turn that is waiting for it. The result is a predictable hook timeout and delayed/failed chat replies.
This is related to the broader active-memory timeout reports such as #65309, #72015, #75843, and #76212, but the local reproduction below isolates a specific lane self-deadlock and a one-line mitigation.
Environment
- OpenClaw:
2026.5.6 (c97b9f7)
- macOS 26.4.1 arm64
- Node service runtime: node 25.9.0
- Gateway: LaunchAgent, local loopback
- Channel used for canary: Telegram direct session
- Main agent model:
openai-codex/gpt-5.5
- Active Memory model:
openai-codex/gpt-5.4-mini
Active Memory config excerpt:
{
"enabled": true,
"agents": ["main"],
"allowedChatTypes": ["direct"],
"queryMode": "recent",
"promptStyle": "balanced",
"timeoutMs": 15000,
"maxSummaryChars": 220,
"persistTranscripts": false,
"logging": true,
"model": "openai-codex/gpt-5.4-mini"
}
Reproduction
- Enable
active-memory for a direct chat session.
- Configure a valid explicit memory model, e.g.
openai-codex/gpt-5.4-mini.
- Restart the gateway.
- Send a simple direct-session canary prompt.
- Observe that
active-memory starts but the hook times out after 15s.
Evidence before local patch
2026-05-07T13:15:31.280-03:00 active-memory: agent=main session=agent:main:telegram:default:direct:<redacted> activeProvider=openai-codex activeModel=gpt-5.4-mini start timeoutMs=15000 queryChars=4381 searchQueryChars=480
2026-05-07T13:15:46.280-03:00 [hooks] before_prompt_build handler from active-memory failed: timed out after 15000ms
2026-05-07T13:15:46.953-03:00 active-memory: agent=main session=agent:main:telegram:default:direct:<redacted> activeProvider=openai-codex activeModel=gpt-5.4-mini done status=timeout elapsedMs=15005 summaryChars=0
2026-05-07T13:15:57.778-03:00 lane task error: lane=main durationMs=0 error="Error: active-memory timeout after 15000ms"
The model was valid and available; changing from the main model to gpt-5.4-mini did not fix the timeout.
Root cause hypothesis
In dist/extensions/active-memory/index.js, runRecallSubagent(...) calls:
params.api.runtime.agent.runEmbeddedPiAgent({
sessionId: subagentSessionId,
sessionKey: subagentSessionKey,
agentId: params.agentId,
...
prompt,
provider: modelRef.provider,
model: modelRef.model,
timeoutMs: embeddedTimeoutMs,
...
});
No lane is passed.
In runEmbeddedPiAgent, the global lane is derived from params.lane; if omitted, it defaults to main:
const globalLane = resolveGlobalLane(params.lane);
resolveGlobalLane(undefined) returns main.
Because active-memory is invoked from before_prompt_build for the user-visible turn, the subagent can be enqueued on the same global main lane that the current turn is holding while waiting for the subagent result. That creates the observed self-block until the hook timeout fires.
Local mitigation tested
Adding a dedicated lane for the active-memory embedded recall avoids the self-block:
params.api.runtime.agent.runEmbeddedPiAgent({
sessionId: subagentSessionId,
sessionKey: subagentSessionKey,
agentId: params.agentId,
...
prompt,
+ lane: "active-memory",
provider: modelRef.provider,
model: modelRef.model,
timeoutMs: embeddedTimeoutMs,
...
});
Evidence after local patch
After applying only that lane change and restarting the gateway, the same internal canary completed. Active-memory no longer timed out:
2026-05-07T13:23:36.260-03:00 active-memory: agent=main session=agent:main:telegram:default:direct:<redacted> activeProvider=openai-codex activeModel=gpt-5.4-mini start timeoutMs=15000 queryChars=533 searchQueryChars=119
2026-05-07T13:23:45.828-03:00 active-memory: agent=main session=agent:main:telegram:default:direct:<redacted> activeProvider=openai-codex activeModel=gpt-5.4-mini done status=empty elapsedMs=9569 summaryChars=0
The outer canary turn returned successfully:
Gateway health afterward was stable: Telegram OK, Discord OK, 0 active/queued/running tasks, event loop OK.
Expected behavior
The active-memory recall subagent should not enqueue itself onto a global lane that is already occupied by the hook waiting for it. It should either use a dedicated lane or otherwise avoid blocking the user-visible run that spawned it.
Actual behavior
Without an explicit lane, active-memory recall can use main, causing the hook to wait for a nested run that cannot start promptly because it is queued behind the parent turn.
Suggested fix
Pass an explicit non-main lane for the active-memory recall embedded run, e.g. lane: "active-memory", or make runEmbeddedPiAgent/hook context detect nested prompt-build use and select a nonblocking lane by default.
A separate follow-up may still be useful for making active-memory fail-open/nonblocking by default, but this lane assignment appears to fix the self-deadlock observed in this environment.
Summary
active-memorycan self-block when it runs its recall subagent from thebefore_prompt_buildhook. The recall call currently does not pass an explicitlanetorunEmbeddedPiAgent, so the embedded runner defaults to the globalmainlane. When the hook itself is already executing as part of a user-visible agent turn onmain, the recall subagent is queued behind the turn that is waiting for it. The result is a predictable hook timeout and delayed/failed chat replies.This is related to the broader active-memory timeout reports such as #65309, #72015, #75843, and #76212, but the local reproduction below isolates a specific lane self-deadlock and a one-line mitigation.
Environment
2026.5.6 (c97b9f7)openai-codex/gpt-5.5openai-codex/gpt-5.4-miniActive Memory config excerpt:
{ "enabled": true, "agents": ["main"], "allowedChatTypes": ["direct"], "queryMode": "recent", "promptStyle": "balanced", "timeoutMs": 15000, "maxSummaryChars": 220, "persistTranscripts": false, "logging": true, "model": "openai-codex/gpt-5.4-mini" }Reproduction
active-memoryfor a direct chat session.openai-codex/gpt-5.4-mini.active-memorystarts but the hook times out after 15s.Evidence before local patch
The model was valid and available; changing from the main model to
gpt-5.4-minidid not fix the timeout.Root cause hypothesis
In
dist/extensions/active-memory/index.js,runRecallSubagent(...)calls:No
laneis passed.In
runEmbeddedPiAgent, the global lane is derived fromparams.lane; if omitted, it defaults tomain:resolveGlobalLane(undefined)returnsmain.Because active-memory is invoked from
before_prompt_buildfor the user-visible turn, the subagent can be enqueued on the same globalmainlane that the current turn is holding while waiting for the subagent result. That creates the observed self-block until the hook timeout fires.Local mitigation tested
Adding a dedicated lane for the active-memory embedded recall avoids the self-block:
params.api.runtime.agent.runEmbeddedPiAgent({ sessionId: subagentSessionId, sessionKey: subagentSessionKey, agentId: params.agentId, ... prompt, + lane: "active-memory", provider: modelRef.provider, model: modelRef.model, timeoutMs: embeddedTimeoutMs, ... });Evidence after local patch
After applying only that lane change and restarting the gateway, the same internal canary completed. Active-memory no longer timed out:
The outer canary turn returned successfully:
Gateway health afterward was stable: Telegram OK, Discord OK, 0 active/queued/running tasks, event loop OK.
Expected behavior
The active-memory recall subagent should not enqueue itself onto a global lane that is already occupied by the hook waiting for it. It should either use a dedicated lane or otherwise avoid blocking the user-visible run that spawned it.
Actual behavior
Without an explicit lane, active-memory recall can use
main, causing the hook to wait for a nested run that cannot start promptly because it is queued behind the parent turn.Suggested fix
Pass an explicit non-main lane for the active-memory recall embedded run, e.g.
lane: "active-memory", or makerunEmbeddedPiAgent/hook context detect nested prompt-build use and select a nonblocking lane by default.A separate follow-up may still be useful for making active-memory fail-open/nonblocking by default, but this lane assignment appears to fix the self-deadlock observed in this environment.