Context
A maintainer's memory-core MCP identity silently regressed to a stale handle (a prior agent name that no longer maps to a seeded AgentIdentity node). Every identity-bound tool — list_messages, query_recent_turns, add_memory, add_message — failed closed with the bare string "no agent identity context bound", naming neither the offending value nor a remediation. Diagnosing it cost a full working session and two harness restarts.
Squarely in the agent-os stability / self-healing focus (parent #13012).
Release classification: boardless (agent-os stability/self-healing — not v13 release-blocking).
Scope (item a): this ticket is the named fail-closed error (legibility). The proactive boot / healthcheck identity-status surface (item b) is split to #13493.
The Problem
The strict, identity-gated A2A / memory-core services reject calls when no AgentIdentity node is bound, throwing a bare "Cannot <op>: no agent identity context bound." that:
- does not name the value it tried,
- does not distinguish unset from set-but-unresolvable (the stale/regressed-handle case), and
- does not point at a remediation.
Two failure classes converge on the same opaque output — a missing var, and a stale/regressed var (what occurred here).
The Architectural Reality
- The hard error originates in the strict identity-gated services — ~16 guard sites that throw
Cannot <op>: no agent identity context bound: ai/services/memory-core/MailboxService.mjs (8), WakeSubscriptionService.mjs (6), PermissionService.mjs (2). Each does if (!RequestContextService.getAgentIdentityNodeId()) throw ….
- The attempted
userId / source remain on the active RequestContextService even when the graph-node binding is null — so the rich message is constructible at the throw sites.
- (Correction of the original draft:
StdioIdentityResolver returns the handle unvalidated, and AuthMiddleware.mjs:94 is the anti-spoof guard — neither is the unbound-error origin. See the pinned correction comment.)
The Fix
A shared RequestContextService.unboundIdentityError(operation) helper that names the attempted handle + source + remediation, distinguishing present-but-unresolvable (stale/renamed/unseeded) from truly-unresolved (single-tenant). Replace the ~16 generic throws with it. The recognizable "no agent identity context bound" prefix is preserved (existing substring/regex assertions hold); fail-closed posture unchanged (message-only).
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| identity-gated services' unbound-error string |
RequestContextService.unboundIdentityError |
Enrich: name value + unset/unresolvable + remediation |
Current fail-closed behavior preserved (message-only) |
inline JSDoc |
this session's opaque-error trace |
Decision Record impact
none — improves diagnostics of the existing identity-resolution design; no change to how identity binds. Aligned with #10692 + #11318.
Acceptance Criteria
Out of Scope
- Boot /
healthcheck identity-status surface (item b) → #13493.
- The harness config-recovery mechanism that restored a stale snapshot (separate harness/operator note).
- Auto-repairing a stale identity; changing how identity binds.
Avoided Traps
- Auto-binding the
gh-fallback identity when the env handle is unresolvable — rejected: silently binding the wrong identity is worse than failing closed; the fix is legibility.
- Relaxing fail-closed — rejected: the security posture is correct; only the diagnostics are deficient.
Related
- Parent epic:
#13012. Item b: #13493. Delivered by PR #13494.
- Adjacent:
#13475, #13289, #13432, #11318.
Handoff Retrieval Hints
- Retrieval Hint:
MailboxService unboundIdentityError no agent identity context bound
Context
A maintainer's memory-core MCP identity silently regressed to a stale handle (a prior agent name that no longer maps to a seeded
AgentIdentitynode). Every identity-bound tool —list_messages,query_recent_turns,add_memory,add_message— failed closed with the bare string "no agent identity context bound", naming neither the offending value nor a remediation. Diagnosing it cost a full working session and two harness restarts.Squarely in the agent-os stability / self-healing focus (parent
#13012).Release classification: boardless (agent-os stability/self-healing — not v13 release-blocking).
Scope (item a): this ticket is the named fail-closed error (legibility). The proactive boot /
healthcheckidentity-status surface (item b) is split to #13493.The Problem
The strict, identity-gated A2A / memory-core services reject calls when no
AgentIdentitynode is bound, throwing a bare"Cannot <op>: no agent identity context bound."that:Two failure classes converge on the same opaque output — a missing var, and a stale/regressed var (what occurred here).
The Architectural Reality
Cannot <op>: no agent identity context bound:ai/services/memory-core/MailboxService.mjs(8),WakeSubscriptionService.mjs(6),PermissionService.mjs(2). Each doesif (!RequestContextService.getAgentIdentityNodeId()) throw ….userId/sourceremain on the activeRequestContextServiceeven when the graph-node binding is null — so the rich message is constructible at the throw sites.StdioIdentityResolverreturns the handle unvalidated, andAuthMiddleware.mjs:94is the anti-spoof guard — neither is the unbound-error origin. See the pinned correction comment.)The Fix
A shared
RequestContextService.unboundIdentityError(operation)helper that names the attempted handle + source + remediation, distinguishing present-but-unresolvable (stale/renamed/unseeded) from truly-unresolved (single-tenant). Replace the ~16 generic throws with it. The recognizable"no agent identity context bound"prefix is preserved (existing substring/regex assertions hold); fail-closed posture unchanged (message-only).Contract Ledger Matrix
RequestContextService.unboundIdentityErrorDecision Record impact
none— improves diagnostics of the existing identity-resolution design; no change to how identity binds. Aligned with#10692+#11318.Acceptance Criteria
Out of Scope
healthcheckidentity-status surface (item b) →#13493.Avoided Traps
gh-fallback identity when the env handle is unresolvable — rejected: silently binding the wrong identity is worse than failing closed; the fix is legibility.Related
#13012. Item b:#13493. Delivered by PR#13494.#13475,#13289,#13432,#11318.Handoff Retrieval Hints
MailboxService unboundIdentityError no agent identity context bound