{"record":{"id":"85216117354551d1","repo":"can1357/oh-my-pi","slug":"output-scan-matchedid-is-not-valid-json-mess","errorCode":null,"errorMessage":"Output ${scan.matchedId} is not valid JSON: ${message}","messagePattern":"Output (.+?) is not valid JSON: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/agent-protocol.ts","lineNumber":113,"sourceCode":"\t\t\tconst availableStr = scan.availableIds.size > 0 ? [...scan.availableIds].join(\", \") : \"none\";\n\t\t\tthrow new Error(`Not found: ${target}\\nAvailable: ${availableStr}`);\n\t\t}\n\n\t\tconst rawContent = await Bun.file(scan.foundPath).text();\n\t\tconst notes: string[] = [];\n\t\tlet content = rawContent;\n\t\tlet contentType: InternalResource[\"contentType\"] = \"text/markdown\";\n\n\t\t// Extraction applies only when the URL did NOT resolve to a nested output\n\t\t// (a slash that named a real child is a hierarchy hop, not a jq path).\n\t\tconst extract = hasQueryExtraction || (hasPathExtraction && scan.matchedId !== nestedId);\n\t\tif (extract) {\n\t\t\tlet jsonValue: unknown;\n\t\t\ttry {\n\t\t\t\tjsonValue = JSON.parse(rawContent);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tthrow new Error(`Output ${scan.matchedId} is not valid JSON: ${message}`);\n\t\t\t}\n\n\t\t\tconst query = hasQueryExtraction ? queryParam! : pathToQuery(urlPath);\n\t\t\tif (query) {\n\t\t\t\tconst extracted = applyQuery(jsonValue, query);\n\t\t\t\ttry {\n\t\t\t\t\tcontent = JSON.stringify(extracted, null, 2) ?? \"null\";\n\t\t\t\t} catch {\n\t\t\t\t\tcontent = String(extracted);\n\t\t\t\t}\n\t\t\t\tnotes.push(`Extracted: ${query}`);\n\t\t\t} else {\n\t\t\t\tcontent = JSON.stringify(jsonValue, null, 2);\n\t\t\t}\n\t\t\tcontentType = \"application/json\";\n\t\t}\n\n\t\treturn {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/agent-protocol.ts#L95-L131","documentation":"AgentProtocolHandler.resolve (packages/coding-agent/src/internal-urls/agent-protocol.ts:113) wraps JSON.parse failures when an agent:// URL requests JSON extraction via a ?q= query or a slash path. It means the artifact file backing the output ID exists but its content is not parseable JSON, so jq-style extraction cannot proceed. The original JSON.parse message (position, unexpected token) is appended to the error.","triggerScenarios":"Calling agent://<id>?q=.field or agent://<id>/path when <id>.md contains plain text/markdown rather than JSON; the output was written by a tool that returns prose, or the extraction form was used on a non-JSON output.","commonSituations":"Pointing ?q= extraction at a markdown report output instead of a JSON tool output; an upstream tool changed its output format from JSON to text between versions; a hand-edited or truncated artifact file; copying an output ID from a different tool whose artifacts are textual.","solutions":["Resolve the bare URL (agent://<id> with no path or ?q=) to inspect the raw content and confirm whether it is JSON.","Remove the ?q= or slash-path extraction from the URL and consume the output as text/markdown.","If JSON is expected, fix the producing tool/agent call so it emits valid JSON (check the appended JSON.parse message for the offending position).","Check the referenced artifact file on disk (sourcePath shown in prior resolves) for truncation or manual edits."],"exampleFix":"// before\nconst out = await resolveUrl(new URL(\"agent://scan_1?q=.findings\"));\n// after — inspect first, extract only if JSON\nconst raw = await resolveUrl(new URL(\"agent://scan_1\"));\nconst url = raw.contentType === \"application/json\"\n  ? new URL(\"agent://scan_1?q=.findings\")\n  : new URL(\"agent://scan_1\");\nconst out2 = await resolveUrl(url);","handlingStrategy":"try-catch","validationCode":"const raw = await resolveUrl(new URL(`agent://${id}`));\nlet parsed: unknown;\ntry { parsed = JSON.parse(raw.content); } catch { parsed = undefined; }\nif (parsed === undefined) {\n  // skip ?q=/path extraction; consume raw.content as text\n}","typeGuard":"function isJsonResource(r: { content: string; contentType: string }): boolean {\n  if (r.contentType !== \"application/json\" && r.contentType !== \"text/markdown\") return false;\n  try { JSON.parse(r.content); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  const out = await resolveUrl(new URL(`agent://${id}?q=.field`));\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(`Output ${id} is not valid JSON`)) {\n    const raw = await resolveUrl(new URL(`agent://${id}`)); // fall back to raw text\n  } else throw err;\n}","preventionTips":["Only add ?q= or slash-path extraction to outputs known to be JSON tool results.","Resolve the bare agent://<id> once to check contentType before extracting.","Never assume an output ID's format; output types vary per producing tool.","Handle both markdown and JSON content types at the call site."],"tags":["json","parsing","internal-urls","agent-protocol"],"backgroundTag":"invalid-json-parse","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}