can1357/oh-my-pi · error · Error
invalid JSON: ${error instanceof Error ? error.message : Str
Error message
invalid JSON: ${error instanceof Error ? error.message : String(error)} What it means
After a successful cargo metadata run, cargoWorkspacePackages() JSON.parses the captured stdout. If parsing fails it rethrows 'invalid JSON: <parse error>'. Since cargo was invoked with --format-version 1, this usually means the stdout was polluted by non-JSON output (warnings, progress lines) or a different cargo wrote human-readable text.
Source
Thrown at packages/coding-agent/src/cleanse/checkers.ts:500
[cargo, "metadata", "--no-deps", "--format-version=1", "--manifest-path", manifest],
{
cwd: path.resolve(state.projectCwd, root),
stderr: "full",
allowNonZero: true,
},
);
if (!result.ok) {
throw new Error(
sanitizeText(result.stderr || result.stdout)
.replace(/\s+/g, " ")
.trim(),
);
}
let parsed: unknown;
try {
parsed = JSON.parse(result.stdout);
} catch (error) {
throw new Error(`invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
}
if (!isRecord(parsed)) throw new Error("metadata root is not an object");
const workspaceMembers = new Set<string>();
if (Array.isArray(parsed.workspace_members)) {
for (const member of parsed.workspace_members) {
if (typeof member === "string") workspaceMembers.add(member);
}
}
const allowedFiles = new Set(state.files);
const packages: string[] = [];
if (!Array.isArray(parsed.packages)) return packages;
for (const value of parsed.packages) {
if (!isRecord(value) || typeof value.id !== "string" || !workspaceMembers.has(value.id)) continue;
if (typeof value.name !== "string" || typeof value.manifest_path !== "string") continue;
const relativeManifest = path.relative(state.projectCwd, value.manifest_path).split(path.sep).join("/");
if (allowedFiles.has(relativeManifest)) packages.push(value.name);
}
return [...new Set(packages)].sort();View on GitHub (pinned to 9690622007)
Solutions
- Run `cargo metadata --format-version 1` manually and confirm stdout is pure JSON
- Remove or bypass wrappers/shims that print to stdout before cargo's JSON
- Update cargo/rustup to a current version that honors --format-version
- If you own the code, parse only the first line that starts with '{' or capture with stderr fully separated
Defensive patterns
Strategy: validation
Validate before calling
function isCargoMetadataJson(stdout: string): boolean {
try {
const v: unknown = JSON.parse(stdout);
return typeof v === "object" && v !== null && !Array.isArray(v);
} catch { return false; }
} Type guard
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Try / catch
try {
return await cargoWorkspacePackages(state);
} catch (err) {
if (err instanceof Error && err.message.startsWith('invalid JSON')) {
logger.warn("cargo metadata produced non-JSON stdout; check cargo wrappers", {});
return [];
}
throw err;
} Prevention
- Keep stdout clean: no wrappers/shims that echo banners around cargo
- Use a current cargo that honors --format-version 1
- Capture stderr separately from stdout when shelling cargo yourself
- Pin/verify the toolchain (rust-toolchain.toml) so environments are consistent
When it happens
Trigger: JSON.parse(result.stdout) throws because cargo printed extra non-JSON text to stdout (build scripts/progress), an old cargo ignores --format-version, or the captured stream mixed stderr into stdout.
Common situations: Custom cargo wrappers or .cargo/config printing banners; very old cargo versions; environment wrappers (e.g. cargo wrapped by sccache/Nix shims) emitting output before the JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- metadata root is not an object
- ${(result.stderr || result.stdout).replace(/\s+/g, " ").trim
- Too many levels of symbolic links
- {}: {error}
- directory stack is empty
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/92b006e73c618e49.
Report an issue: GitHub.