[Bug] TypeError in insights command: Object.keys() called on undefined value

Resolved 💬 16 comments Opened Feb 4, 2026 by michellemayes Closed Feb 13, 2026
💡 Likely answer: A maintainer (wolffiex, collaborator) responded on this thread — see the highlighted reply below.

Bug Description
/insights
⎿ TypeError: undefined is not an object (evaluating 'Object.keys(b)')

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.1.31
  • Feedback ID: 86f6ee30-02b6-4477-9b06-1abcbe765559

Errors

[{"error":"Error: LSP server plugin:pyright-lsp:pyright failed to start: Executable not found in $PATH: \"pyright-langserver\"\n    at start (/$bunfs/root/claude:2167:22315)\n    at async H (/$bunfs/root/claude:2167:24763)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:27.216Z"},{"error":"Error: Executable not found in $PATH: \"pyright-langserver\"\n    at spawn (unknown)\n    at spawn (node:child_process:667:35)\n    at spawn (node:child_process:14:39)\n    at start (/$bunfs/root/claude:2167:20845)\n    at start (/$bunfs/root/claude:2167:22369)\n    at H (/$bunfs/root/claude:2167:24763)\n    at H (/$bunfs/root/claude:2167:25738)\n    at _ (/$bunfs/root/claude:2167:32525)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:27.216Z"},{"error":"Error: Failed to start LSP server plugin:pyright-lsp:pyright: Executable not found in $PATH: \"pyright-langserver\"\n    at <anonymous> (/$bunfs/root/claude:2167:32547)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:27.216Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKCyooFVg8U8bGo1gQ\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:34.752Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKDKePrHf2XE2pLDjk\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:34.942Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKCxKda5918vu3Vgw7\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:35.111Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKCyopxKRUED7hW4F3\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:35.137Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKEBjd4Rowbps4TzwT\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:35.143Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKCw5oU5bVZqSyKy38\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:35.421Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKGgJbVQXB1WyJ9Qqt\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:36.535Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKHo2LrRt3NfziuahS\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-04T16:46:36.951Z"},{"error":"Error: 500 {\"type\":\"error\",\"error\":{\"type\":\"api_error\",\"message\":\"Internal server error\"},\"request_id\":\"req_011CXoNKNq93caqQZq8ejnfr\"}\n    at generate (/$bunfs/root/claude:344:8942)\n    at makeRequest (/$bunfs/root/claude:361:5435)\n    at processTicksAndRejections (native:7:39)","timestamp…

Note: Content was truncated.

View original on GitHub ↗

16 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/23137
  2. https://github.com/anthropics/claude-code/issues/22998
  3. https://github.com/anthropics/claude-code/issues/14620

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

jkleinvt · 5 months ago

Same here, other folks on my team getting this as well.

muhammadbimo1 · 5 months ago

Also got this error: TypeError: undefined is not an object (evaluating 'Object.keys(h)')
I appear to be billed tokens for what they fetched before they're trying to display, though.

debugtheworldbot · 5 months ago

Same here with v2.1.31

Mr-Neutr0n · 5 months ago

Confirming same issue on macOS. Running /insights throws:

TypeError: undefined is not an object (evaluating 'Object.keys(b)')

Version: 2.1.31 (Claude Code)

abrahamgreyson · 5 months ago

Same here, MacOS 2.1.31 installed from Brew.

nickcernis · 5 months ago

/insights also fails in 2.1.31 on macOS for me when export CLAUDE_CODE_USE_VERTEX=1.

It works fine when I use it on another machine with a Claude Pro account.

Running /debug immediately after the failed /insights when running Claude via Vertex AI gives this:

This indicates that Claude Code is trying to use a beta feature (structured-outputs-2025-12-15) that's not currently available or has an incorrect header value. This error occurred when you ran the /insights command.

Reference:

Migrating from beta? The output_format parameter has moved to output_config.format, and beta headers are no longer required. The old beta header (structured-outputs-2025-11-13) and output_format parameter will continue working for a transition period. See code examples below for the updated API shape. — https://platform.claude.com/docs/en/build-with-claude/structured-outputs

Full debug output:

 /insights
  ⎿  TypeError: undefined is not an object (evaluating 'Object.keys(b)')

❯ /debug

⏺ I'll analyze the debug log to identify any issues in your current Claude Code session.

⏺ Search(pattern: "\[ERROR\]", path: ".claude/debug/f6a7491d-dd2e-496d-b13d-9b8bf522a846.txt")
  ⎿  Found 2 lines (ctrl+o to expand)

⏺ Search(pattern: "\[WARN\]", path: ".claude/debug/f6a7491d-dd2e-496d-b13d-9b8bf522a846.txt")
  ⎿  Found 0 lines

⏺ Based on the debug log analysis, I found two key issues:

  Issues Found

  1. API Error - Invalid Beta Header

  The main error is an API request failure with a 400 status code:

  Unexpected value(s) `structured-outputs-2025-12-15` for the `anthropic-beta` header

  This indicates that Claude Code is trying to use a beta feature (structured-outputs-2025-12-15) that's not currently available or has an incorrect header value. This error occurred when you
  ran the /insights command.

  2. /insights Command Failure

  The /insights command failed with:
  TypeError: undefined is not an object (evaluating 'Object.keys(b)')

  This JavaScript error suggests the insights feature is trying to access properties on an undefined object, likely as a consequence of the API error above.

  What This Means

  The /insights skill appears to be incompatible with the current API or is using outdated beta headers. The structured outputs feature it's trying to use either:
  - Has been renamed or deprecated
  - Requires a different header value
  - Isn't available on your account/plan

  Suggested Fixes

  1. Update Claude Code: Run claude update to get the latest version, which may have the correct API headers.
  2. Check for Plugin Updates: The insights command might be from a plugin that needs updating. Check your installed plugins.
  3. Wait and Retry: This could be a temporary API issue that resolves itself.
  4. Avoid /insights for now: Until this is fixed, avoid using the /insights command.
stephanretool · 5 months ago

Just ran into this myself for /insights, and got claude to heal itself. Maybe other features have similar/same root cause, so leaving some analysis in case it is helpful for anyone.

Issue in my case was OAuth (into a Claude Enterprise subscription account, in case that matters) expiring resulting in API errors, for example:

{
       "type": "error",
       "error": {
         "type": "authentication_error",
         "message": "OAuth token has expired. Please obtain a new token or refresh your existing token."
       },
       "request_id": "req_xxx",
       "session_id": "xxxx"
     }

/insights did not check for/notice that this happened and cached error responses into ~/.claude/usage-data/facets/. Even after logging back in (which made other features work again), the error when invoking /insights persisted because it kept trying to read prior facet files (which contained said errors still). /facets seems to not be able to handle the json format of the error responses.

Running grep -l '"type": "error"' ~/.claude/usage-data/facets/*.json | xargs rm -v fixed it for me. Think for yourself if you want to copy paste this command as is of course, but it should only remove prior facet files with errors that /insights created. If I understand correctly, these are "cache" files with analysis of prior sessions. Even removing all of them should in worst case only result in the next /insights run taking a bit longer (as it will regenerate them) and should not affect anything else.

orangewise · 5 months ago

had to run the command slightly different: grep -l '"error":' ~/.claude/usage-data/facets/*.json | xargs rm -v

Ryadlotfimahtal · 5 months ago

Running this command worked for me also : grep -l '"error":' ~/.claude/usage-data/facets/*.json | xargs rm -v

frankbria · 5 months ago

Additional troubleshooting that did NOT resolve the issue

Environment: Linux (WSL2), Claude Code v2.1.31

Steps tried:

  1. Removed corrupted facet cache file (per @stephanretool's suggestion):

grep -l '"error":' ~/.claude/usage-data/facets/*.json | xargs rm -v

  1. This removed one file containing an error response, but /insights immediately failed again.
  2. Cleared ALL facet cache files:

rm ~/.claude/usage-data/facets/*.json

  1. Same result - /insights fails immediately and recreates corrupted cache files.
  2. Checked session history files for encoding issues - scanned all .jsonl files in ~/.claude/projects/ for invalid Unicode

surrogates - none found.

Debug log analysis:

The debug log shows the actual API errors being returned:

  [ERROR] API error (attempt 1/11): 400 {"type":"error","error":{"type":"invalid_request_error","message":"The request body is not
  valid JSON: no low surrogate in string: line 1 column 3340 (char 3339)"}}

This "no low surrogate in string" error repeats for many parallel requests (15+ times in quick succession), suggesting /insights is
firing off multiple API calls that all fail due to malformed JSON in the request body.

Conclusion:

The issue appears to be in how Claude Code v2.1.31 constructs the API request body for /insights, not in cached files. The request
contains invalid Unicode (unpaired surrogate characters) that the API rejects. Clearing caches doesn't help because the bug is in
the request construction.

This may be related to @nickcernis's finding about the structured-outputs-2025-12-15 beta header issue, or it could be a separate
encoding bug.

smmhrdmn · 5 months ago

Running this command worked for me also : grep -l '"error":' ~/.claude/usage-data/facets/*.json | xargs rm -v

atani · 5 months ago

Root Cause Analysis

I investigated this issue in v2.1.33 and identified the exact root cause by decompiling the binary.

Where the crash happens

In the minified code, the Q filter function (used to filter out "warmup_minimal" sessions) does:

let w = G.get(F);        // Get facet from Map
if (!w) return !1;        // Skip if no facet
let b = w.goal_categories; // Get goal_categories
u = Object.keys(b)...    // CRASH if b is undefined

The guard if (!w) return !1 only checks if the facet object itself is falsy. But when the facet API call fails (e.g., invalid_request_error due to surrogate characters in session data, or 500 Internal server error), the error response object is saved as the facet cache file and also kept in the in-memory Map. Since the error response {"type":"error","error":{...}} is truthy, it passes the guard, but w.goal_categories is undefined, causing the TypeError.

The bug in SN8 (facet save function)

The function jN8 extracts facets via API. When the API returns an error, jN8 still returns the parsed response. SN8 then saves it to ~/.claude/usage-data/facets/<session_id>.json without validating that it's actually a valid facet object. On subsequent /insights runs, MN8 reads this cached error response and treats it as a valid facet.

How to reproduce

  1. Have a session with content that causes the facet extraction API to fail (e.g., unpaired surrogate characters, or just an API 500 error)
  2. Run /insights — the error response gets cached as a facet file
  3. /insights crashes when iterating over the cached error response

Workaround

Delete or fix corrupted facet files in ~/.claude/usage-data/facets/:

python3 -c "
import json, os, glob
facets_dir = os.path.expanduser('~/.claude/usage-data/facets')
for f in glob.glob(os.path.join(facets_dir, '*.json')):
    with open(f) as fh:
        data = json.load(fh)
    if not isinstance(data.get('goal_categories'), dict):
        sid = os.path.basename(f).replace('.json', '')
        valid = {'session_id': sid, 'goal_categories': {}, 'friction_counts': {},
                 'user_satisfaction_counts': {}, 'outcome': 'unclear_from_transcript',
                 'claude_helpfulness': 'unsure', 'session_type': 'unknown',
                 'primary_success': 'none', 'brief_summary': 'Facet extraction failed',
                 'underlying_goal': 'unknown'}
        with open(f, 'w') as fh:
            json.dump(valid, fh)
        print(f'Fixed: {os.path.basename(f)}')
"

Note: You may also need to pre-create facet files for sessions that don't have cached facets yet, since /insights will attempt to generate them (up to 50 per run) and any API failure will reproduce the crash. Pre-creating all missing facets prevents the problematic API calls entirely.

Suggested fix

In SN8, validate that the object has goal_categories as a dict before saving. In the Q function, add a check: if (!w?.goal_categories) return !1.

Environment

  • Version: 2.1.33
  • Platform: macOS (darwin, arm64)
fkztw · 5 months ago

Workaround Verified: Pre-creating Facet Files Successfully Resolves the Issue

I can confirm that the workaround suggested by @atani works perfectly. Here's my experience and verification results:

Environment

  • Version: 2.1.34
  • Platform: macOS (darwin, arm64)
  • Node.js: v24.6.0
  • Total sessions: 180
  • Initial facet cache: 35 files

Root Cause Confirmation

@atani's analysis is spot-on. The issue occurs because:

  1. Sessions containing invalid Unicode (unpaired surrogate characters) cause the facet extraction API to return 400 errors:

``
invalid_request_error: "no low surrogate in string: line 1 column 4192"
``

  1. Claude Code's SN8 function saves the API error response as a facet file without validation
  1. On subsequent /insights runs, the error response object {"type":"error","error":{...}} passes the if (!w) check (because it's truthy), but w.goal_categories is undefined, causing the TypeError

Workaround Implementation

I pre-created valid facet files for all 145 missing sessions using the suggested Python script. Key points:

  • Empty goal_categories: {} is safe in JavaScript — Object.keys({}) returns [] without crashing
  • This prevents /insights from attempting API calls that would fail and cache error responses

Validation Results

After applying the workaround:

✅ Total facet files: 180 / 180 (100% coverage)
✅ Valid goal_categories: 180 / 180
✅ Error response files: 0
✅ /insights execution: SUCCESS

The /insights command now works perfectly and generated a full usage report analyzing all 30 sessions.

Complete Workaround Script

For anyone hitting this issue, here's the complete script that worked for me:

import json, os, glob

facets_dir = os.path.expanduser('~/.claude/usage-data/facets')
sessions_dir = os.path.expanduser('~/.claude/projects')

# Collect all session IDs
session_ids = set()
for jsonl_file in glob.glob(os.path.join(sessions_dir, '**/*.jsonl'), recursive=True):
    sid = os.path.basename(jsonl_file).replace('.jsonl', '')
    session_ids.add(sid)

# Find missing facet files
existing_facets = {os.path.basename(f).replace('.json', '')
                   for f in glob.glob(os.path.join(facets_dir, '*.json'))}
missing = session_ids - existing_facets

print(f'Creating {len(missing)} missing facet files...')

# Pre-create facet files for missing sessions
for sid in missing:
    facet_path = os.path.join(facets_dir, f'{sid}.json')
    valid_facet = {
        'session_id': sid,
        'goal_categories': {},
        'friction_counts': {},
        'user_satisfaction_counts': {},
        'outcome': 'unclear_from_transcript',
        'claude_helpfulness': 'unsure',
        'session_type': 'unknown',
        'primary_success': 'none',
        'brief_summary': 'Pre-created to avoid API failure',
        'underlying_goal': 'unknown'
    }
    with open(facet_path, 'w') as fh:
        json.dump(valid_facet, fh, indent=2)

print(f'✅ Created {len(missing)} facet files')

Additional Notes

  • Not related to Node.js v24: Initially suspected but ruled out through testing
  • Safe for existing data: The workaround doesn't corrupt existing valid facet files
  • CJK characters: Sessions with Chinese/Japanese/Korean characters, emojis, or special Unicode may trigger this issue

Suggested Permanent Fix

As @atani suggested, the fix should be in two places:

In SN8 (facet save function):

// Validate response before saving
if (response?.goal_categories && typeof response.goal_categories === 'object') {
  saveFacet(response);
} else {
  console.error('Invalid facet response, not caching');
}

In Q (filter function):

let w = G.get(F);
if (!w?.goal_categories) return !1;  // Defensive check
let b = w.goal_categories;
u = Object.keys(b)...

Hope this helps others hitting the same issue! 🎉

wolffiex collaborator · 5 months ago

This is late but we fixed this in a recent release. Let me know if this isn't fixed and thanks for the report!

github-actions[bot] · 4 months ago

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.