{"record":{"id":"0aedcbf733657b35","repo":"can1357/oh-my-pi","slug":"invalid-xd-url-url-href-use-xd-or-xd","errorCode":null,"errorMessage":"Invalid xd:// URL: ${url.href}. Use xd:// or xd://<tool>.","messagePattern":"Invalid xd:// URL: (.+?)\\. Use xd:// or xd://<tool>\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/xd-protocol.ts","lineNumber":34,"sourceCode":"\treturn { name };\n}\n\n/** Whether a streaming path prefix could still become an `xd://` URL. */\nexport function couldBecomeXdUrl(partialPath: string): boolean {\n\tif (partialPath.length <= XD_URL_PREFIX.length) {\n\t\treturn XD_URL_PREFIX.startsWith(partialPath.toLowerCase());\n\t}\n\treturn partialPath.toLowerCase().startsWith(XD_URL_PREFIX);\n}\n\n/** Routes session-bound virtual tool devices through `xd://` URLs. */\nexport class XdProtocolHandler implements ProtocolHandler {\n\treadonly scheme = \"xd\";\n\treadonly immutable = true;\n\n\tasync resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {\n\t\tconst target = parseXdUrl(url.href);\n\t\tif (!target) throw new Error(`Invalid xd:// URL: ${url.href}. Use xd:// or xd://<tool>.`);\n\t\tif (!context?.xd) throw new Error(\"xd:// is not mounted in this session.\");\n\t\tconst content = await context.xd.read(target.name);\n\t\treturn { url: url.href, content, contentType: \"text/plain\", size: Buffer.byteLength(content) };\n\t}\n\n\tasync write(url: InternalUrl, content: string, context?: WriteContext): Promise<void> {\n\t\tconst target = parseXdUrl(url.href);\n\t\tif (!target) throw new Error(`Invalid xd:// URL: ${url.href}. Use xd://<tool>.`);\n\t\tif (!context?.xd) throw new Error(\"xd:// is not mounted in this session.\");\n\t\tawait context.xd.write(target.name, content);\n\t}\n}\n","sourceCodeStart":16,"sourceCodeEnd":47,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/xd-protocol.ts#L16-L47","documentation":"XdProtocolHandler.resolve() validates the incoming URL with parseXdUrl(), which accepts only \"xd://\" (root) or \"xd://<tool>\" with no path separators, query, or fragment characters. Anything else (wrong scheme, subpaths like xd://a/b, trailing /?# characters) makes the parser return null and the handler throws this error. It is pure URL-shape validation before any device access.","triggerScenarios":"Resolving an InternalUrl whose href is not exactly xd:// or xd://<name> — e.g. \"xd://browser/tab/1\" (contains \"/\"), \"xd://tool?x=1\" (contains \"?\"), or a non-xd URL routed to this handler.","commonSituations":"Appending paths or query strings to xd:// URLs as if they were HTTP URLs; agents constructing hierarchical device addresses; routing a foreign URL into the xd handler.","solutions":["Use \"xd://\" for the device root or \"xd://<tool>\" for a single tool device — no subpaths, query, or fragments.","Pre-validate with parseXdUrl(url.href) and handle null before calling resolve.","If you need hierarchical addressing, encode it in the tool name or use a different protocol."],"exampleFix":"// before\nawait resolveInternalUrl(\"xd://browser/tab/1\"); // path -> throws\n// after\nawait resolveInternalUrl(\"xd://browser\"); // valid device URL","handlingStrategy":"validation","validationCode":"import { parseXdUrl } from \"./internal-urls/xd-protocol\";\nif (!parseXdUrl(url.href)) {\n  throw new Error(`Not a valid xd:// device URL: ${url.href}. Use xd:// or xd://<tool>.`);\n}","typeGuard":"function isXdDeviceUrl(href: string): boolean {\n  return /^xd:\\/\\/[^/?#]*$/.test(href.trim());\n}","tryCatchPattern":"try {\n  return await resolveInternalUrl(url);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid xd:// URL:\")) {\n    // normalize: strip subpath/query, or route to the correct protocol handler\n    return;\n  }\n  throw err;\n}","preventionTips":["Remember xd:// device names are flat: no '/', '?', or '#' allowed.","Run parseXdUrl() on any externally supplied xd:// URL before resolving.","Never treat xd:// like an HTTP URL with paths or query strings."],"tags":["url-validation","xd","input-validation"],"backgroundTag":"invalid-url-format","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}