{"record":{"id":"007b40b41cfbbaa0","repo":"can1357/oh-my-pi","slug":"invalid-memory-glob-url-input","errorCode":null,"errorMessage":"Invalid memory glob URL: ${input}","messagePattern":"Invalid memory glob URL: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/memory-protocol.ts","lineNumber":72,"sourceCode":"/**\n * Decode percent-escapes in a raw glob-suffix segment, bracket-escaping any\n * glob metacharacter that was percent-encoded so it stays a literal filename\n * character instead of becoming glob syntax.\n */\nfunction decodeGlobSuffixSegment(rawSegment: string): string {\n\t// Escape runs are decoded together so multi-byte UTF-8 sequences survive.\n\treturn rawSegment.replace(/(?:%[0-9a-f]{2})+/gi, run => decodeURIComponent(run).replace(/[*?[{]/g, \"[$&]\"));\n}\n\n/**\n * Split a memory:// glob at its first wildcard after validating the complete\n * decoded path. The suffix is validated before filesystem globbing so `..`\n * cannot escape a safely resolved base directory.\n */\nexport function splitMemoryGlobPattern(input: string): MemoryGlobPattern {\n\tconst urlMatch = input.match(/^([a-z][a-z0-9+.-]*:\\/\\/[^/?#]*)(\\/.*)?$/i);\n\tif (!urlMatch) {\n\t\tthrow new Error(`Invalid memory glob URL: ${input}`);\n\t}\n\n\t// Parse only the scheme and authority. A literal `?` in the path is glob\n\t// syntax, not a query delimiter, and must survive unchanged.\n\tconst url = parseInternalUrl(urlMatch[1]);\n\tconst namespace = url.rawHost || url.hostname;\n\tif (url.protocol !== \"memory:\" || namespace !== MEMORY_NAMESPACE) {\n\t\tthrow new Error(`Memory glob patterns require the ${MEMORY_NAMESPACE} namespace: ${input}`);\n\t}\n\n\tconst rawPathname = urlMatch[2] ?? \"\";\n\tif (/%(?:2f|5c)/i.test(rawPathname)) {\n\t\tthrow new Error(`Encoded path separators are not allowed in memory:// glob patterns: ${input}`);\n\t}\n\n\tlet relativePath: string;\n\ttry {\n\t\trelativePath = decodeURIComponent(rawPathname.replace(/^\\//, \"\"));","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/memory-protocol.ts#L54-L90","documentation":"splitMemoryGlobPattern first parses the input as scheme://authority followed by an optional /path, using a strict regex. Inputs that don't start with a valid scheme://authority — or are otherwise malformed as a URL — throw this error before any filesystem work happens.","triggerScenarios":"Calling memoryGlob/splitMemoryGlobPattern with a bare path like 'notes/*.md', a Windows-style path, a scheme-less glob, or a string with whitespace/characters that break the ^scheme://[^/?#]* structure.","commonSituations":"Hand-writing glob inputs and forgetting the memory://root prefix; passing a plain filesystem glob from config; an LLM emitting `memory:notes/*.md` or `memories/*.md` instead of a full URL; shell quoting stripping the scheme.","solutions":["Prefix the input with the full URL form: memory://root/<path-with-glob>, e.g. memory://root/notes/*.md.","Validate the shape client-side with the same pattern (a scheme, ://, non-path authority, then /path) before calling.","If input comes from an agent, correct the tool prompt/examples so memory globs are always fully qualified memory:// URLs."],"exampleFix":"// before\nconst { baseUrl, globPattern } = splitMemoryGlobPattern(\"notes/*.md\");\n// after\nconst { baseUrl, globPattern } = splitMemoryGlobPattern(\"memory://root/notes/*.md\");","handlingStrategy":"validation","validationCode":"const URL_SHAPE = /^([a-z][a-z0-9+.-]*:\\/\\/[^/?#]*)(\\/.*)?$/i;\nfunction isParsableMemoryGlobUrl(input: string): boolean {\n  return URL_SHAPE.test(input.trim());\n}\n// call splitMemoryGlobPattern only if isParsableMemoryGlobUrl(input)","typeGuard":"function looksLikeUrl(input: string): boolean {\n  return /^[a-z][a-z0-9+.-]*:\\/\\//i.test(input);\n}","tryCatchPattern":"let parsed;\ntry {\n  parsed = splitMemoryGlobPattern(input);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Invalid memory glob URL\")) {\n    return splitMemoryGlobPattern(`memory://root/${input.replace(/^\\/+/, \"\")}`); // repair missing scheme\n  }\n  throw e;\n}","preventionTips":["Always pass fully qualified memory://root/... URLs to glob APIs — never bare paths.","Trim and shell-quote inputs so whitespace or quoting cannot corrupt the scheme.","Constrain agent tool args to memory:// URLs with schema validation.","Document the required URL shape where globs are configured."],"tags":["url-parsing","memory","validation"],"backgroundTag":"malformed-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}