{"record":{"id":"456238312a63bb04","repo":"can1357/oh-my-pi","slug":"agent-url-cannot-combine-path-extraction-with","errorCode":null,"errorMessage":"agent:// URL cannot combine path extraction with ?q=","messagePattern":"agent:// URL cannot combine path extraction with \\?q=","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/agent-protocol.ts","lineNumber":49,"sourceCode":" * with optional JSON extraction.\n */\nexport class AgentProtocolHandler implements ProtocolHandler {\n\treadonly scheme = \"agent\";\n\treadonly immutable = true;\n\n\tasync resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {\n\t\tconst outputId = url.rawHost || url.hostname;\n\t\tif (!outputId) {\n\t\t\tthrow new Error(\"agent:// URL requires an output ID: agent://<id>\");\n\t\t}\n\n\t\tconst urlPath = url.pathname;\n\t\tconst queryParam = url.searchParams.get(\"q\");\n\t\tconst hasPathExtraction = urlPath && urlPath !== \"/\" && urlPath !== \"\";\n\t\tconst hasQueryExtraction = queryParam !== null && queryParam !== \"\";\n\n\t\tif (hasPathExtraction && hasQueryExtraction) {\n\t\t\tthrow new Error(\"agent:// URL cannot combine path extraction with ?q=\");\n\t\t}\n\n\t\tconst registry = AgentRegistry.global();\n\t\tconst rootSessionFile = context?.sessionFile\n\t\t\t? await ensurePersistedRoster(registry, context.sessionFile)\n\t\t\t: undefined;\n\t\t// The caller root's canonical artifact directory (its session file minus\n\t\t// the `.jsonl` suffix) is scanned FIRST, ahead of every process-global\n\t\t// registry dir. The roster ref this refresh installs for the caller's\n\t\t// parked id contributes only its nested child dir, not the root dir that\n\t\t// actually holds `<id>.md` — and with two coexisting roots the global\n\t\t// `Main` ref can belong to the other root, whose dir would otherwise win\n\t\t// the first-hit id map for a shared id. No caller session file: keep the\n\t\t// pre-existing global scan untouched.\n\t\tconst dirs = artifactsDirsFromRegistry(\n\t\t\trootSessionFile ? { preferredDir: rootSessionFile.slice(0, -6) } : undefined,\n\t\t);\n\t\tif (dirs.length === 0) {","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/agent-protocol.ts#L31-L67","documentation":"agent:// URLs support two mutually exclusive extraction mechanisms: a slash path (agent://<id>/<path>) and a query string (agent://<id>?q=<query>). If a URL supplies both a non-trivial pathname and a non-empty ?q= parameter, the handler cannot decide which extraction to apply and throws. This is a strict ambiguity check on URL shape.","triggerScenarios":"Calling resolve() with a URL that has both a pathname other than '' and '/', and a non-empty 'q' search param — e.g. 'agent://out1/foo/bar?q=.items[0]' or 'agent://out1/data.json?q=results'.","commonSituations":"Appending ?q= to a URL that already contains a path-based extraction (copy-pasted from an example that used the other form); code that programmatically appends query params to a base URL that already has path segments; migrating between the two extraction forms and leaving both in place.","solutions":["Remove the ?q= parameter and express the extraction as a path, keeping one mechanism","Or remove the path portion so only ?q= remains: agent://<id>?q=<query>","If building URLs dynamically, strip the pathname when a q param is present (or vice versa)"],"exampleFix":"// before\nawait handler.resolve(parseUrl(\"agent://out1/foo/bar?q=.items[0]\"), ctx);\n// after\nawait handler.resolve(parseUrl(\"agent://out1?q=.foo.bar.items[0]\"), ctx);","handlingStrategy":"validation","validationCode":"const hasPath = url.pathname && url.pathname !== \"/\";\nconst hasQuery = url.searchParams.get(\"q\") !== null && url.searchParams.get(\"q\") !== \"\";\nif (hasPath && hasQuery) throw new Error(\"agent:// URL cannot combine path extraction with ?q=\");","typeGuard":"function usesSingleExtraction(url: InternalUrl): boolean {\n  const hasPath = Boolean(url.pathname && url.pathname !== \"/\");\n  const q = url.searchParams.get(\"q\");\n  const hasQuery = q !== null && q !== \"\";\n  return !(hasPath && hasQuery);\n}","tryCatchPattern":null,"preventionTips":["Pick one extraction form per URL: path OR ?q=, never both","When appending ?q= programmatically, clear the pathname first","Document the two URL forms (path extraction vs query extraction) for URL builders"],"tags":["url","validation","ambiguity","agent-protocol"],"backgroundTag":"conflicting-url-parameters","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}