can1357/oh-my-pi · error
agent:// URL requires an output ID: agent://<id>
Error message
agent:// URL requires an output ID: agent://<id>
What it means
AgentProtocolHandler.resolve() resolves agent:// URLs to stored agent output artifacts. The output ID is taken from the URL host (url.rawHost || url.hostname); if the URL has no host component, there is nothing to look up, so the handler throws immediately. This is a malformed-URL guard, not a lookup failure.
Source
Thrown at packages/coding-agent/src/internal-urls/agent-protocol.ts:40
import { ensurePersistedRoster } from "../registry/persisted-agents";
import { applyQuery, pathToQuery } from "./json-query";
import { artifactsDirsFromRegistry } from "./registry-helpers";
import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext, UrlCompletion } from "./types";
/**
* Handler for agent:// URLs.
*
* Resolves output IDs like "reviewer_0" to their artifact files,
* with optional JSON extraction.
*/
export class AgentProtocolHandler implements ProtocolHandler {
readonly scheme = "agent";
readonly immutable = true;
async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
const outputId = url.rawHost || url.hostname;
if (!outputId) {
throw new Error("agent:// URL requires an output ID: agent://<id>");
}
const urlPath = url.pathname;
const queryParam = url.searchParams.get("q");
const hasPathExtraction = urlPath && urlPath !== "/" && urlPath !== "";
const hasQueryExtraction = queryParam !== null && queryParam !== "";
if (hasPathExtraction && hasQueryExtraction) {
throw new Error("agent:// URL cannot combine path extraction with ?q=");
}
const registry = AgentRegistry.global();
const rootSessionFile = context?.sessionFile
? await ensurePersistedRoster(registry, context.sessionFile)
: undefined;
// The caller root's canonical artifact directory (its session file minus
// the `.jsonl` suffix) is scanned FIRST, ahead of every process-global
// registry dir. The roster ref this refresh installs for the caller'sView on GitHub (pinned to 9690622007)
Solutions
- Put the output ID in the URL host position: agent://<id> (e.g. agent://reviewer_0)
- Use the path only for nested subagent ids or JSON extraction: agent://<id>/<child> or agent://<id>?q=<query>
- If building the URL in code, validate the id is non-empty before constructing it
Example fix
// before
await handler.resolve({ host: "", pathname: "/reviewer_0" } as InternalUrl, ctx);
// after
await handler.resolve({ host: "reviewer_0", pathname: "" } as InternalUrl, ctx); Defensive patterns
Strategy: validation
Validate before calling
const outputId = url.rawHost || url.hostname;
if (!outputId) throw new Error(`agent:// URL requires an output ID: got '${url.href}'`); Type guard
function hasOutputId(url: InternalUrl): boolean {
return Boolean(url.rawHost || url.hostname);
} Prevention
- Always author agent:// URLs as agent://<id> with the id in the host position
- Validate ids are non-empty before interpolating into URL templates
- Use handler.complete() to get valid id values for completion UIs
When it happens
Trigger: Calling resolve() with an InternalUrl whose rawHost and hostname are both empty — e.g. 'agent://', 'agent:///foo/bar', or a URL string that was parsed such that the id landed in the path instead of the host.
Common situations: Hand-writing an agent:// URL and putting the id in the path so the host ends up empty (e.g. 'agent:///reviewer_0'); constructing the InternalUrl programmatically with a missing host field; template strings that interpolate an undefined/empty id before the host.
Related errors
- agent:// URL cannot combine path extraction with ?q=
- Provider delete URL must not embed an account credential
- ${destination} returned an invalid upload URL
- Destination option endpoint must be an absolute URL
- Destination option endpoint must use HTTP or HTTPS
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/20973a84d1c86251.
Report an issue: GitHub.