can1357/oh-my-pi · error · ToolError
e instanceof Error ? e.message : String(e) (wraps parseInter
Error message
e instanceof Error ? e.message : String(e) (wraps parseInternalUrl error)
What it means
Before resolving an agent:// (or other internal) URL with query extraction, ReadTool parses it via parseInternalUrl. Malformed URLs (bad scheme shape, unparseable host/colons) throw inside parseInternalUrl and are re-wrapped as a ToolError carrying the underlying message.
Source
Thrown at packages/coding-agent/src/tools/read.ts:2188
/**
* Handle internal URLs (agent://, artifact://, memory://, skill://, rule://, local://, mcp://).
* Supports pagination via offset/limit but rejects them when query extraction is used.
*/
async #handleInternalUrl(
url: string,
parsedSel: ParsedSelector,
signal?: AbortSignal,
): Promise<AgentToolResult<ReadToolDetails>> {
const internalRouter = InternalUrlRouter.instance();
// Check if URL has query extraction (agent:// only).
// Use parseInternalUrl which handles colons in host (namespaced skills).
let urlMeta: InternalUrl;
try {
urlMeta = parseInternalUrl(url);
} catch (e) {
throw new ToolError(e instanceof Error ? e.message : String(e));
}
const scheme = urlMeta.protocol.replace(/:$/, "").toLowerCase();
let hasExtraction = false;
if (scheme === "agent") {
const hasPathExtraction = urlMeta.pathname && urlMeta.pathname !== "/" && urlMeta.pathname !== "";
const queryParam = urlMeta.searchParams.get("q");
const hasQueryExtraction = queryParam !== null && queryParam !== "";
hasExtraction = hasPathExtraction || hasQueryExtraction;
}
if (scheme === "artifact") {
return this.#readArtifactFile(urlMeta, parsedSel, signal);
}
// local:// files are real on-disk paths. Detect image files and emit a
// decoded image block before the text-only resource contract UTF-8
// decodes the binary into mojibake. The fast path returns null for
// non-images, directories, listings, or any resolution failure, so the
// text path below reproduces the router's not-found / symlink-escapeView on GitHub (pinned to 9690622007)
Solutions
- Fix the URL structure: scheme://host/path with query params for extraction (e.g. agent://skill?q=...).
- For skills with colons in names, follow the namespaced-host form supported by parseInternalUrl.
- Validate the URL with new URL(...) or the same parser before calling read.
Example fix
// before
read("agent://my:skill/doc")
// after
read("agent://my.skill/doc") // or namespaced host form accepted by parseInternalUrl Defensive patterns
Strategy: validation
Validate before calling
try { new URL(url.replace(/^agent:/, 'https:')); } catch { throw new Error('Malformed internal URL: ' + url); } Type guard
function isParsableInternalUrl(url) { try { parseInternalUrl(url); return true; } catch { return false; } } Try / catch
try { return await read(url) } catch (e) { if (e instanceof ToolError && !('Invalid selector' in e.message)) { /* log and rebuild URL from parts */ } throw e; } Prevention
- Build internal URLs from a helper that joins scheme/host/path safely
- Escape colons in namespaced skill hosts per convention
- Round-trip URLs through parseInternalUrl during construction
When it happens
Trigger: Reading a URL like 'agent://::bad' or a URL whose host/path structure parseInternalUrl cannot parse — e.g. missing host, illegal colon placement in namespaced skill urls.
Common situations: Agent constructs resource URLs programmatically and mis-joins segments; skill names containing colons are not escaped per the namespaced-host convention; copy-pasted URLs mangled.
Related errors
- transparent (brush_parser::BindingParseError)
- Custom URL is required for option 3
- Custom URL is required for option 3
- Provider delete URL must not embed an account credential
- ${destination} returned an invalid upload URL
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5fe00fc2b54eb00e.
Report an issue: GitHub.