Skip to content

mcp/channel-bridge: pendingClaudePermissions / pendingApprovals leak — no TTL, no close-clear, no cap #71646

Description

@Feelw00

Summary

OpenClawChannelBridge (src/mcp/channel-bridge.ts) holds two instance-bound pending Maps that lack the same three guards their siblings already have, so a long-running openclaw mcp serve process accumulates entries monotonically.

Collection Cap Close-clear TTL / sweeper
queue (Array, L48) QUEUE_LIMIT=1000 while-shift (L355) n/a n/a
pendingWaiters (Set, L49) n/a close() clears (L148) per-waiter setTimeout fallback (L265)
pendingClaudePermissions (Map, L50) none none none
pendingApprovals (Map, L51) none none expiresAtMs is stored at L382 but never drives a timer/sweeper

Why it matters

  • pendingClaudePermissions: Claude SDK sends a notifications/claude/channel/permission_request for every tool call (channel-server.ts:42-49). The entry is only removed when the operator replies with yes <id> / no <id> matching /^(yes|no)\s+([a-km-z]{5})$/i (channel-bridge.ts:455-459). Missed/typo'd/cross-channel responses leave the entry forever. requestId is fresh per request so there is no overwrite.
  • pendingApprovals: trackApproval (L369-) sets the entry on exec.approval.requested / plugin.approval.requested and only deletes on the matching *.resolved event (L389). A gateway WebSocket drop (channel-bridge.ts:122-124 onClose is reject-only) silently loses the resolved frame and the entry persists.
  • close() (L136-152) clears pendingWaiters but does not clear either pending Map.

In a long-running openclaw mcp serve process (operator hours-units, claudeChannelMode != 'off' busy automation), both Maps grow monotonically.

Affected paths

  • src/mcp/channel-bridge.ts:50 (pendingClaudePermissions decl)
  • src/mcp/channel-bridge.ts:51 (pendingApprovals decl)
  • src/mcp/channel-bridge.ts:136-152 (close() body — pending Map clears missing)
  • src/mcp/channel-bridge.ts:273-295 (handleClaudePermissionRequest set without TTL)
  • src/mcp/channel-bridge.ts:369-391 (trackApproval stores expiresAtMs but no expiry mechanism)

Severity

P2 — slow memory-growth leak in opt-in long-running CLI path. Not an immediate OOM, but unbounded over time. Maintainer-priority axes hit: memory + reliability + plugin loading (MCP).

Notes

  • AI-assisted (openclaw-audit pipeline, FIND-mcp-memory-001/002 → CAND-025 epic).
  • 5-agent post-harness cross-review and 3-agent pre-PR cross-review both proceed (consensus on real problem + fix sufficient).
  • Upstream-dup check clean: 6w channel-bridge.ts commits all refactor/seam-split (no leak axis); PR fix: bind Claude permission replies to session #56420 (sessionKey binding, OPEN) is orthogonal.
  • Fix PR follows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions