{"record":{"id":"8af19007a13ff985","repo":"can1357/oh-my-pi","slug":"metadata-root-is-not-an-object","errorCode":null,"errorMessage":"metadata root is not an object","messagePattern":"metadata root is not an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cleanse/checkers.ts","lineNumber":502,"sourceCode":"\t\t\tcwd: path.resolve(state.projectCwd, root),\n\t\t\tstderr: \"full\",\n\t\t\tallowNonZero: true,\n\t\t},\n\t);\n\tif (!result.ok) {\n\t\tthrow new Error(\n\t\t\tsanitizeText(result.stderr || result.stdout)\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim(),\n\t\t);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(result.stdout);\n\t} catch (error) {\n\t\tthrow new Error(`invalid JSON: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (!isRecord(parsed)) throw new Error(\"metadata root is not an object\");\n\tconst workspaceMembers = new Set<string>();\n\tif (Array.isArray(parsed.workspace_members)) {\n\t\tfor (const member of parsed.workspace_members) {\n\t\t\tif (typeof member === \"string\") workspaceMembers.add(member);\n\t\t}\n\t}\n\tconst allowedFiles = new Set(state.files);\n\tconst packages: string[] = [];\n\tif (!Array.isArray(parsed.packages)) return packages;\n\tfor (const value of parsed.packages) {\n\t\tif (!isRecord(value) || typeof value.id !== \"string\" || !workspaceMembers.has(value.id)) continue;\n\t\tif (typeof value.name !== \"string\" || typeof value.manifest_path !== \"string\") continue;\n\t\tconst relativeManifest = path.relative(state.projectCwd, value.manifest_path).split(path.sep).join(\"/\");\n\t\tif (allowedFiles.has(relativeManifest)) packages.push(value.name);\n\t}\n\treturn [...new Set(packages)].sort();\n}\n","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cleanse/checkers.ts#L484-L520","documentation":"cargo metadata can theoretically return JSON whose root is not an object (e.g. an array or scalar). The function validates with isRecord(parsed) and throws 'metadata root is not an object' before accessing parsed.workspace_members, defending the subsequent property access.","triggerScenarios":"JSON.parse succeeded but produced a non-object root — practically only when the captured stdout is valid JSON from something other than cargo metadata, or a mocked/stubbed cargo returns unexpected JSON.","commonSituations":"Test stubs or wrapper scripts emitting a JSON array/string; intercepted output from a different tool; extremely unusual cargo versions. Rarely seen with real cargo metadata, which always returns an object.","solutions":["Verify what command actually produced the stdout (check for wrappers/shims on the cargo path)","Run `cargo metadata --format-version 1 | jq type` — it should print '\"object\"'; if not, find the interfering tool","If a wrapper emits a JSON array, fix the wrapper to emit the cargo metadata object","Update cargo to a standard distribution version"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"const parsed: unknown = JSON.parse(stdout);\nif (!isRecord(parsed) || !Array.isArray(parsed.workspace_members)) {\n  throw new Error(\"stdout is not a cargo metadata object\");\n}","typeGuard":"function isCargoMetadata(v: unknown): v is { workspace_members?: unknown[] } & Record<string, unknown> {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  return await cargoWorkspacePackages(state);\n} catch (err) {\n  if (err instanceof Error && err.message === \"metadata root is not an object\") {\n    logger.warn(\"unexpected cargo metadata shape; skipping rust discovery\", {});\n    return [];\n  }\n  throw err;\n}","preventionTips":["Validate JSON shape before consuming fields whenever parsing tool output","Ensure only real cargo metadata output reaches the parser (no stubs/wrappers)","Keep cargo versions standard so the schema stays an object","Add smoke coverage for the Rust discovery path in CI"],"tags":["rust","cargo","json-parsing","validation"],"backgroundTag":"invalid-json-output","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}