can1357/oh-my-pi · error
Output ${scan.matchedId} is not valid JSON: ${message}
Error message
Output ${scan.matchedId} is not valid JSON: ${message} What it means
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.
Source
Thrown at packages/coding-agent/src/internal-urls/agent-protocol.ts:113
const availableStr = scan.availableIds.size > 0 ? [...scan.availableIds].join(", ") : "none";
throw new Error(`Not found: ${target}\nAvailable: ${availableStr}`);
}
const rawContent = await Bun.file(scan.foundPath).text();
const notes: string[] = [];
let content = rawContent;
let contentType: InternalResource["contentType"] = "text/markdown";
// Extraction applies only when the URL did NOT resolve to a nested output
// (a slash that named a real child is a hierarchy hop, not a jq path).
const extract = hasQueryExtraction || (hasPathExtraction && scan.matchedId !== nestedId);
if (extract) {
let jsonValue: unknown;
try {
jsonValue = JSON.parse(rawContent);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Output ${scan.matchedId} is not valid JSON: ${message}`);
}
const query = hasQueryExtraction ? queryParam! : pathToQuery(urlPath);
if (query) {
const extracted = applyQuery(jsonValue, query);
try {
content = JSON.stringify(extracted, null, 2) ?? "null";
} catch {
content = String(extracted);
}
notes.push(`Extracted: ${query}`);
} else {
content = JSON.stringify(jsonValue, null, 2);
}
contentType = "application/json";
}
return {View on GitHub (pinned to 9690622007)
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.
Example fix
// before
const out = await resolveUrl(new URL("agent://scan_1?q=.findings"));
// after — inspect first, extract only if JSON
const raw = await resolveUrl(new URL("agent://scan_1"));
const url = raw.contentType === "application/json"
? new URL("agent://scan_1?q=.findings")
: new URL("agent://scan_1");
const out2 = await resolveUrl(url); Defensive patterns
Strategy: try-catch
Validate before calling
const raw = await resolveUrl(new URL(`agent://${id}`));
let parsed: unknown;
try { parsed = JSON.parse(raw.content); } catch { parsed = undefined; }
if (parsed === undefined) {
// skip ?q=/path extraction; consume raw.content as text
} Type guard
function isJsonResource(r: { content: string; contentType: string }): boolean {
if (r.contentType !== "application/json" && r.contentType !== "text/markdown") return false;
try { JSON.parse(r.content); return true; } catch { return false; }
} Try / catch
try {
const out = await resolveUrl(new URL(`agent://${id}?q=.field`));
} catch (err) {
if (err instanceof Error && err.message.startsWith(`Output ${id} is not valid JSON`)) {
const raw = await resolveUrl(new URL(`agent://${id}`)); // fall back to raw text
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Replacement text is not valid UTF-8: {err}
- V2 compaction stream parse failed: ${err instanceof Error ?
- Gemini Files API ${context} response is not valid JSON
- No JSON payload found in response
- Failed to parse top stories
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/85216117354551d1.
Report an issue: GitHub.