{"record":{"id":"eb530b54d1cc2ca1","repo":"can1357/oh-my-pi","slug":"invalid-url-encoding-in-memory-path-url-href","errorCode":null,"errorMessage":"Invalid URL encoding in memory:// path: ${url.href}","messagePattern":"Invalid URL encoding in memory:// path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/memory-protocol.ts","lineNumber":135,"sourceCode":"export function resolveMemoryUrlToPath(url: InternalUrl, memoryRoot: string): string {\n\tconst namespace = url.rawHost || url.hostname;\n\tif (!namespace) {\n\t\tthrow new Error(\"memory:// URL requires a namespace: memory://root\");\n\t}\n\tif (namespace !== MEMORY_NAMESPACE) {\n\t\tthrow new Error(`Unknown memory namespace: ${namespace}. Supported: ${MEMORY_NAMESPACE}`);\n\t}\n\n\tconst rawPathname = url.rawPathname ?? url.pathname;\n\tconst hasPath = rawPathname && rawPathname !== \"/\" && rawPathname !== \"\";\n\tif (!hasPath) {\n\t\treturn path.resolve(memoryRoot, DEFAULT_MEMORY_FILE);\n\t}\n\tlet relativePath: string;\n\ttry {\n\t\trelativePath = decodeURIComponent(rawPathname.slice(1));\n\t} catch {\n\t\tthrow new Error(`Invalid URL encoding in memory:// path: ${url.href}`);\n\t}\n\n\ttry {\n\t\tvalidateRelativePath(relativePath);\n\t} catch (error) {\n\t\tthrow toMemoryValidationError(error);\n\t}\n\n\treturn path.resolve(memoryRoot, relativePath);\n}\n\nasync function tryResolveInRoot(url: InternalUrl, memoryRoot: string): Promise<InternalResource | undefined> {\n\tconst resolved = path.resolve(memoryRoot);\n\tlet resolvedRoot: string;\n\ttry {\n\t\tresolvedRoot = await fs.realpath(resolved);\n\t} catch (error) {\n\t\tif (isEnoent(error)) return undefined;","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/memory-protocol.ts#L117-L153","documentation":"After stripping the leading slash, the memory:// pathname is percent-decoded with decodeURIComponent. If the escapes are malformed (e.g. a dangling '%' or a truncated '%E4' sequence), decoding throws and the resolver rethrows this error naming the offending URL, rather than producing a corrupted path.","triggerScenarios":"resolveMemoryUrlToPath receiving a pathname with invalid percent-encoding, such as memory://root/notes%.md or memory://root/%E4%B8 (truncated UTF-8 escape), usually from hand-written or shell-mangled URLs.","commonSituations":"Copy-pasting a URL where a '%' got eaten by a shell; double-encoding mistakes (writing %2520 then partially decoding); tools that interpolate raw strings containing '%' into the path.","solutions":["Percent-encode special characters properly: encode '%' as %25 and non-ASCII as valid UTF-8 escapes before putting them in the URL.","Use encodeURIComponent on dynamic path segments when building memory:// URLs programmatically.","If the '%' is meant literally in a filename, escape it as %25, e.g. memory://root/100%25done.md."],"exampleFix":"// before\nread(`memory://root/${rawName}`) // rawName = \"50% off.md\"\n// after\nread(`memory://root/${encodeURIComponent(rawName)}`)","handlingStrategy":"validation","validationCode":"const pathname = url.rawPathname ?? url.pathname;\ntry { decodeURIComponent(pathname.slice(1)); } catch { throw new Error(`Invalid percent-encoding: ${url.href}`); }","typeGuard":null,"tryCatchPattern":"try {\n  const p = resolveMemoryUrlToPath(url, root);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid URL encoding\")) {\n    // re-encode the path segment and retry once\n  } else throw err;\n}","preventionTips":["encodeURIComponent every dynamic path segment before composing memory:// URLs.","Escape literal '%' in filenames as %25.","Never paste URLs through shells that strip or mangle '%'."],"tags":["url","encoding","memory-protocol"],"backgroundTag":"invalid-url-encoding","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}