can1357/oh-my-pi · error
Invalid xd:// URL: ${url.href}. Use xd://<tool>.
Error message
Invalid xd:// URL: ${url.href}. Use xd://<tool>. What it means
XdProtocolHandler.write validates the target URL before delegating to the session's xd device writer. parseXdUrl returns null for anything that is not an xd:// URL or that contains path/query/fragment characters (/, ?, #), so write throws with guidance to use a plain device URL. Note the root form 'xd://' (empty name) is valid for resolve but this message steers writers toward xd://<tool> because writing to the root device is not meaningful.
Source
Thrown at packages/coding-agent/src/internal-urls/xd-protocol.ts:42
return partialPath.toLowerCase().startsWith(XD_URL_PREFIX);
}
/** Routes session-bound virtual tool devices through `xd://` URLs. */
export class XdProtocolHandler implements ProtocolHandler {
readonly scheme = "xd";
readonly immutable = true;
async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
const target = parseXdUrl(url.href);
if (!target) throw new Error(`Invalid xd:// URL: ${url.href}. Use xd:// or xd://<tool>.`);
if (!context?.xd) throw new Error("xd:// is not mounted in this session.");
const content = await context.xd.read(target.name);
return { url: url.href, content, contentType: "text/plain", size: Buffer.byteLength(content) };
}
async write(url: InternalUrl, content: string, context?: WriteContext): Promise<void> {
const target = parseXdUrl(url.href);
if (!target) throw new Error(`Invalid xd:// URL: ${url.href}. Use xd://<tool>.`);
if (!context?.xd) throw new Error("xd:// is not mounted in this session.");
await context.xd.write(target.name, content);
}
}
View on GitHub (pinned to 9690622007)
Solutions
- Use a bare device name URL of the form xd://<tool> with no slashes, query, or fragment after the name
- Verify the URL actually has the xd:// scheme before handing it to this handler (route other schemes to their own handlers)
- Trim or sanitize the device name so stray characters like ? or # are removed
Example fix
// before
await handler.write({ href: 'xd://mytool/output?raw=1' } as InternalUrl, data, ctx); // throws
// after
await handler.write({ href: 'xd://mytool' } as InternalUrl, data, ctx); Defensive patterns
Strategy: validation
Validate before calling
function isValidXdWriteUrl(href: string): boolean {
const target = parseXdUrl(href);
return target !== null && target.name !== null; // named device, root 'xd://' not writable
} Try / catch
try {
await handler.write(url, content, ctx);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Invalid xd:// URL')) {
logger.warn('Rejected xd write URL', { href: url.href });
return;
}
throw err;
} Prevention
- Always build xd URLs from the device name alone: `xd://${name}`
- Never append paths, query strings, or fragments to xd:// URLs
- Route only xd-scheme URLs to XdProtocolHandler; use a scheme dispatcher for everything else
- Escape or reject device names containing /, ?, or # at the source (device registration)
When it happens
Trigger: Calling write() with url.href that is not an xd:// URL at all (different scheme, leading whitespace is trimmed so that is OK), or an xd:// URL containing /, ?, or # — e.g. xd://tool/subpath, xd://tool?key=1, xd://tool#frag.
Common situations: Passing a regular file:// or https:// URL to the xd handler by routing mistake; appending a path or query string to an xd:// URL out of habit from http URLs; constructing the URL by string concatenation that leaves a trailing slash or fragment.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- artifact:// URL requires a numeric ID: artifact://0
- artifact:// ID must be numeric, got: ${id}
- xd:// is not mounted in this session.
- resultBaseUrl is not a valid URL
- Destination option ${optionName} must use http or https
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/65116f2ae9572a52.
Report an issue: GitHub.