Skip to content

Agent permission response silently lost after ~15 min idle — UI desync between GitLens and Claude Code #5230

@nikolay-1986

Description

@nikolay-1986

Describe the bug

When a Claude Code agent requests permission (e.g., git pull), the request is displayed both in the Claude Code terminal and in the GitLens Graph UI. If the user waits ~15 minutes before responding via the GitLens Graph UI (Allow button), the following happens:

  1. GitLens resolves the permission internally and updates the session state to "Working"
  2. Claude Code never receives the response — the terminal remains stuck at the permission prompt
  3. The user sees a desync: GitLens says the agent is working, but Claude Code is still waiting for input

The only recovery is to respond directly in the Claude Code terminal or restart the session.

Steps to reproduce

  1. Enable gitlens.graph.experimentalFeatures.enabled: true
  2. Start a Claude Code agent session on a branch in the workspace
  3. Trigger an action that requires permission (e.g., a git pull Bash command)
  4. Observe the permission request appears in both:
    • Claude Code terminal: "This command requires approval — Do you want to proceed?"
    • GitLens Graph: agent pill changes to "Needs input" with Allow/Deny buttons
  5. Wait approximately 15 minutes without responding
  6. Click "Allow" in the GitLens Graph UI
  7. Observe: GitLens transitions to "Working", but Claude Code terminal remains stuck

Expected behavior

Either:

  • The permission response should be successfully delivered to Claude Code regardless of wait time, OR
  • GitLens should detect the stale/dead connection and show an error state instead of transitioning to "Working"

Root cause analysis

The issue stems from three gaps in the IPC response delivery chain:

1. No error handling on HTTP response (packages/ipc/src/ipcServer.ts:88-169)

The onRequest() handler calls res.end(JSON.stringify(result)) without:

  • res.on('error') handler — silent failure if socket is broken
  • res.on('close') listener — no detection of client disconnect
  • req.on('close') or req.on('aborted') listener — no detection of request abort

The response write is fire-and-forget with no delivery confirmation.

2. Optimistic state update (packages/plus/agents/src/providers/claudeCodeProvider.ts:600-629)

resolvePermission() updates session state to "Working" before (and without) confirming the HTTP response was delivered:

Line 611: pending.resolve({ ... })     // resolves the IPC handler Promise
Line 622: _pendingPermissions.delete() // cleans up local state
Line 627: updateSessionStatus()        // transitions to "Working" in UI

The resolve() call only completes the handler's Promise return — it does not verify the TCP payload reached the client.

3. No connection health monitoring

  • No TCP keep-alive configured on the IPC server
  • No heartbeat mechanism between hook process and server
  • After ~15 minutes of idle, the OS-level TCP timeout silently drops the connection
  • Neither side detects the dead socket until a write is attempted (and even then, the error is unhandled)

Suggested fix directions

  1. Detect dead connections proactively: Add req.on('close') / req.on('aborted') listeners in the IPC server. When a client disconnects while a blocking handler is pending, reject the pending permission and revert session state.

  2. Verify delivery before state transition: Wrap res.end() with error handling. Only update session state to "Working" after confirming the response was flushed to the socket (e.g., via the res.end() callback or res.writableFinished).

  3. Add connection keep-alive: Configure TCP keep-alive on the IPC server socket to detect dead connections earlier than the OS default (~15 min).

  4. Add a timeout on the pending permission Promise: If no response is delivered within N minutes, auto-reject the permission and update the UI to reflect the timeout. This prevents the session from being held indefinitely in a stale state.

Environment

  • GitLens pre-release 2026.5.140551
  • Windows 11
  • Claude Code (CLI) via terminal
  • gitlens.graph.experimentalFeatures.enabled: true

Metadata

Metadata

Assignees

Labels

area-graphIssues or features related to the Commit Graph

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions