PostToolUse:Edit IDE diagnostics: 'code' field returns literal '[object Object]' instead of the diagnostic code string
Summary
When the VS Code extension surfaces IDE diagnostics back to the model via the PostToolUse:Edit hook (the <ide_diagnostics> block in the tool result), the per-diagnostic code field is a literal string "[object Object]" instead of the actual analyzer code (e.g. "unused_import", "discarded_futures", "undefined_method").
This is a JS-side stringification bug: the underlying Diagnostic.code from the language server is being sent through String(code) (or template-literal coercion) without checking whether code is the LSP "object form" {value: string | number, target: Uri}.
Reproduction
- Open a Dart file in VS Code (Dart Code extension installed and active).
- Use the Claude Code Edit tool to introduce a Dart analyzer warning — e.g., remove a use of an imported symbol so
unused_importfires, or addasyncto a function thatawaits nothing sounnecessary_asyncfires. - Inspect the
PostToolUse:Edithook output that comes back to the model. The<ide_diagnostics>block contains entries like:
{
"filePath": "/c:/src/.../my_file.dart",
"line": 42,
"column": 5,
"message": "Unused import: 'package:flutter/foundation.dart'.\nTry removing the import directive.",
"code": "[object Object]",
"severity": "Warning"
}
The message field is correctly populated. The code field is wrong.
Expected
{
...,
"code": "unused_import",
...
}
Or, if code is genuinely the LSP rich form {value, target}, then either:
- Pass through as
{ value: "unused_import", target: "https://dart.dev/diagnostics/unused_import" }, OR - Extract
.valueto a string before serialization.
Why this matters
The code field is what downstream code uses for case-dispatch / categorization. When it's the literal string "[object Object]":
- The model can't reliably reason about diagnostic categories (it sees the same code for everything, only the message differs).
- Any UI / bridge component that does
switch (diagnostic.code)hits its default arm — and if that default arm throws, the failure surfaces asUnhandled case: [object Object](#59013, #59047, #59067, etc.). - The remote-control bridge (#59158) appears to be one such consumer that wedges when fed a stream of these malformed payloads.
Likely cause
VS Code's Diagnostic.code API has two valid shapes per the API docs:
code?: string | number | { value: string | number; target: Uri };
Many language servers (Dart Code included) use the rich {value, target} form so that diagnostics link to documentation. If the extension's diagnostic-serialization layer does:
const code = String(diagnostic.code); // or `${diagnostic.code}` — same thing
…it produces "[object Object]" for the rich form, because plain JS objects have no custom toString().
The fix is a one-liner:
const code = typeof diagnostic.code === 'object' && diagnostic.code !== null
? String(diagnostic.code.value)
: String(diagnostic.code ?? '');
Environment
- VS Code on Windows 11 (build 26200)
- Claude Code extension (latest stable as of 2026-05-14)
- Dart Code extension active on a Flutter project
- Reproduced consistently — every Edit to a Dart file in this session produced this output
Cross-references
- #59158 — VS Code remote-control half-state. Combined with this bug, the malformed
codepayload appears to be what wedges the remote-control bridge serializer, causing theUnhandled case: [object Object]cascade. - #59013 / #59047 / #59067 — likely downstream symptoms (the
Unhandled casebanner is the user-visible artifact when the bridge serializer chokes on the malformed payload).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗