Stale permission popup causes repeated IPC errors, UI freeze, and blank window after restart
Description
Accomplish can become permanently unresponsive when a permission popup remains visible after its underlying permission request has expired or the task has already completed.
Clicking Allow or Deny on the stale popup can send repeated permission.respond RPC calls. The daemon rejects responses because the request is no longer pending or the task is no longer active. After this loop, the Electron renderer becomes unresponsive. Closing and reopening the application may produce a blank window because the old GUI and daemon processes remain alive with a stale Unix socket.
Environment
- Accomplish:
0.5.17 - macOS: Apple Silicon
- Electron:
41.2.0 - Model observed:
GLM-4.7 - OpenCode runtimes tested: packaged
1.4.3and local1.18.11
The problem appears to be in Accomplish’s permission lifecycle and is not specific to GLM-4.7.
Steps to reproduce
- Start a task that requests file permission.
- Leave the permission popup unanswered until the request expires or the task completes.
- Return to the still-visible popup.
- Click Allow several times, or click Allow and then Deny.
- Observe repeated IPC errors.
- Close and reopen Accomplish.
Actual behavior
- The stale popup remains visible.
- Every response generates an RPC error.
- The response may be submitted repeatedly.
- The UI becomes unresponsive.
- Reopening Accomplish can show an empty window.
- The previous GUI and daemon processes remain alive.
- The existing
daemon.pidanddaemon.sockcan continue referencing the stale daemon.
Relevant error:
[Daemon] Permission response for unmatched requestId: filereq_...
[DaemonRpcServer] Handler error for permission.respond:
No pending permission request with id: filereq_...
[ipc] IPC handler permission:respond failed
Error: No pending permission request with id: filereq_...
[main] Error occurred in handler for 'permission:respond':
{"code":-32603}The same response was emitted repeatedly for the same requestId.
Expected behavior
- A permission popup must be dismissed when its request expires, is cancelled, or its task completes.
- A late or duplicate response should be treated as idempotent.
- The UI should not repeatedly submit the same response.
- An unmatched permission response should not produce a fatal RPC rejection.
- Closing and reopening the app should not reuse an orphaned daemon.
Suspected root cause
The permission response path rejects stale task/request IDs. In the current source, apps/daemon/src/daemon-routes.ts checks that the task is active before forwarding to taskService.sendResponse; the downstream adapter also rejects when no pending permission/question request exists.
There appears to be missing synchronization between:
- Permission-request expiration.
- Task cleanup/completion.
- Popup lifecycle in the renderer.
- Duplicate response prevention.
The renderer continues holding the popup after the daemon removes the pending request.
Proposed fix
Treat late or duplicate permission responses as idempotent at the appropriate boundary:
- Close the popup when the request expires or the task completes.
- Disable its buttons immediately after the first response.
- Track resolved request IDs temporarily to distinguish duplicates from unknown IDs.
- Add a renderer-side guard against repeated submissions.
- Ensure orphaned daemon processes and stale sockets are detected during startup.
- Preserve a successful response contract for late/duplicate responses where safe, while keeping observable diagnostics for genuinely unknown task IDs.
Local workaround and validation
A local patch changed unmatched responses from Promise.reject(...) to Promise.resolve().
After terminating the orphaned GUI/daemon processes and relaunching:
- The full interface loaded correctly.
- A GLM-4.7 smoke task completed with
OK. - The OpenCode process exited with code
0. - No new permission IPC errors appeared during the fresh smoke test.
The exact stale-popup expiration path has not yet been reproduced end-to-end after applying the patch, so a regression test is still recommended.
Suggested regression tests
- Permission request expires while the popup is visible.
- Task completes while a popup remains open.
- User double-clicks Allow.
- User clicks Allow followed by Deny.
- Renderer reconnects to the daemon with a stale popup.
- Application restarts after a permission-response error.
- Duplicate responses return success without changing the original decision.
Source: accomplish-ai/coworker