claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

Performance Degradation: Keyboard Input Responsiveness Decreases Over Time

Open lukehutch opened this issue 8 months ago • 2 comments

Bug Description Keyboard entry gets laggier and jankier the longer I use Claude Code. In particular hitting the delete button can cease working for up to 30 seconds at a time, even while typing new characters works intermittently.

Environment Info

  • Platform: macos
  • Terminal: Apple_Terminal
  • Version: 1.0.7
  • Feedback ID: 210e2f28-46af-4052-ac05-0f6ecef38588

Errors

[{"error":"Error: Command failed: security find-generic-password -a $USER -w -s \"Claude Code\"\nsecurity: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.\n\n    at genericNodeError (node:internal/errors:983:15)\n    at wrappedFn (node:internal/errors:537:14)\n    at checkExecSyncError (node:child_process:882:11)\n    at execSync (node:child_process:954:15)\n    at BZ (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:638:3921)\n    at file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:566:6930\n    at Q (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:526:17069)\n    at fl (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:566:5949)\n    at YG (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:566:5580)\n    at DV1 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1611:6607)","timestamp":"2025-06-02T08:09:47.156Z"},{"error":"ShellError: Shell command failed\n    at Object.call (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1510:1326)\n    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n    at async S55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:12788)\n    at async qV1 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:11316)\n    at async T55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10640)\n    at async O55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10574)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:9535)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)","timestamp":"2025-06-02T08:10:41.240Z"},{"error":"ShellError: Shell command failed\n    at Object.call (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1510:1326)\n    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n    at async S55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:12788)\n    at async qV1 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:11316)\n    at async T55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10640)\n    at async O55 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10574)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:9535)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)\n    at async Ve (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:1846:10171)","timestamp":"2025-06-02T08:10:42.169Z"},{"error":"Error: Request was aborted.\n    at Pr._createMessage (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:725:6810)\n    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)","timestamp":"2025-06-02T08:10:46.757Z"}]

lukehutch avatar Jun 02 '25 08:06 lukehutch

Weirdly this does not seem to be as bad for Opus as it is for Sonnet.

lukehutch avatar Jun 02 '25 08:06 lukehutch

Eventually Claude Code grinds to a halt and node crashes with OOM (so there seems to be some sort of memory leak in Claude Code, because repeated compacting of the session does not lower the heap used).

<--- Last few GCs --->

[96564:0x130008000] 70611463 ms: Mark-Compact (reduce) 4089.7 (4097.4) -> 4089.3 (4097.7) MB, pooled: 0 MB, 593.29 / 0.00 ms  (+ 11.2 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 632 ms) (average mu = 0.729, c[96564:0x130008000] 70612196 ms: Mark-Compact 4090.3 (4097.7) -> 4090.0 (4101.4) MB, pooled: 0 MB, 732.83 / 0.00 ms  (average mu = 0.547, current mu = 0.000) allocation failure; scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

 1: 0x102ddf7c0 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 2: 0x102f893f4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 3: 0x103146424 v8::internal::Heap::CallGCPrologueCallbacks(v8::GCType, v8::GCCallbackFlags, v8::internal::GCTracer::Scope::ScopeId) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 4: 0x103143a80 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 5: 0x10313d0a4 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 6: 0x10313da6c v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 7: 0x10312eb18 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 8: 0x1033f1954 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
 9: 0x102bd39f4 Builtins_CEntry_Return1_ArgvOnStack_NoBuiltinExit [/opt/homebrew/Cellar/node/23.9.0/bin/node]
10: 0x102b3f62c Builtins_GrowFastSmiOrObjectElements [/opt/homebrew/Cellar/node/23.9.0/bin/node]
11: 0x10c4d9758 
12: 0x10c4559c8 
13: 0x10b8713e8 
14: 0x10bf2a038 
15: 0x10bf2c004 
16: 0x10bd48294 
17: 0x10b72bfac 
18: 0x10c4c2928 
19: 0x10c3577a8 
20: 0x10bf3d280 
21: 0x10c4d32a8 
22: 0x10c4c26fc 
23: 0x10c3577a8 
24: 0x10b742aac 
25: 0x10b7d6c0c 
26: 0x102b3a50c Builtins_JSEntryTrampoline [/opt/homebrew/Cellar/node/23.9.0/bin/node]
27: 0x102b3a1b0 Builtins_JSEntry [/opt/homebrew/Cellar/node/23.9.0/bin/node]
28: 0x1030bb344 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
29: 0x1030bacac v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
30: 0x102f9ff34 v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
31: 0x102cf56d4 node::InternalCallbackScope::Close() [/opt/homebrew/Cellar/node/23.9.0/bin/node]
32: 0x102cf5b94 node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context, v8::Local<v8::Value>) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
33: 0x102d0dd90 node::AsyncWrap::MakeCallback(v8::Local<v8::Function>, int, v8::Local<v8::Value>*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
34: 0x102ef76b4 node::StreamBase::CallJSOnreadMethod(long, v8::Local<v8::ArrayBuffer>, unsigned long, node::StreamBase::StreamBaseJSChecks) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
35: 0x102ef7938 node::EmitToJSStreamListener::OnStreamRead(long, uv_buf_t const&) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
36: 0x102efdd20 node::LibuvStreamWrap::OnUvRead(long, uv_buf_t const*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
37: 0x102efda28 node::LibuvStreamWrap::ReadStart()::$_1::__invoke(uv_stream_s*, long, uv_buf_t const*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
38: 0x106536e6c uv__stream_io [/opt/homebrew/Cellar/libuv/1.50.0/lib/libuv.1.dylib]
39: 0x10653e0e0 uv__io_poll [/opt/homebrew/Cellar/libuv/1.50.0/lib/libuv.1.dylib]
40: 0x10652df08 uv_run [/opt/homebrew/Cellar/libuv/1.50.0/lib/libuv.1.dylib]
41: 0x102cf6488 node::SpinEventLoopInternal(node::Environment*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
42: 0x102e26f10 node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
43: 0x102e26c64 node::NodeMainInstance::Run() [/opt/homebrew/Cellar/node/23.9.0/bin/node]
44: 0x102da027c node::Start(int, char**) [/opt/homebrew/Cellar/node/23.9.0/bin/node]
45: 0x1995deb98 start [/usr/lib/dyld]

lukehutch avatar Jun 03 '25 06:06 lukehutch

I'm experiencing the exact same issue on macOS with claude CLI. The problem manifests in two ways:

  1. Interactive REPL lag: When typing in the interactive mode (claude), there's significant keystroke delay - characters appear in batches rather than instantly
  2. Task execution slowdown: While command execution being slow is somewhat acceptable, the input lag makes the tool nearly unusable

Environment:

  • OS: macOS Tahoe 26.0
  • Terminal: Tested on both:
    • Alacritty + tmux
    • VSCode integrated terminal
  • Claude CLI version: [run claude --version and add here]
  • The issue persists across different terminal environments

Additional observations:

  • The --print mode works without lag: echo "prompt" | claude --print
  • JSON streaming mode (--output-format json) also works fine but loses interactive features (subagents, todos, etc.)
  • The problem seems to be in the interactive event loop handling terminal I/O
  • This is NOT terminal-specific as it happens in both Alacritty/tmux and VSCode

Current workaround: Using rlwrap with print mode: rlwrap bash -c 'while read p; do echo "$p" | claude --print; done'

This appears to be an architectural issue with how the interactive mode handles async operations alongside terminal input. The event loop seems to be blocking on API calls, causing the keystroke buffering.

+1 to @lukehutch's description - the delete key behavior is particularly frustrating, sometimes taking 30+ seconds to respond.

Has anyone found a better workaround that preserves the interactive features?

K-NRS avatar Aug 05 '25 10:08 K-NRS

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.

github-actions[bot] avatar Aug 13 '25 14:08 github-actions[bot]