{"record":{"id":"b02430a63a618c40","repo":"can1357/oh-my-pi","slug":"memory-url-must-resolve-to-a-file-or-directory","errorCode":null,"errorMessage":"memory:// URL must resolve to a file or directory: ${url.href}","messagePattern":"memory:// URL must resolve to a file or directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/memory-protocol.ts","lineNumber":185,"sourceCode":"\t\t}\n\t}\n\n\tlet realTargetPath: string;\n\ttry {\n\t\trealTargetPath = await fs.realpath(targetPath);\n\t} catch (error) {\n\t\tif (isEnoent(error)) return undefined;\n\t\tthrow error;\n\t}\n\n\tensureWithinRoot(realTargetPath, resolvedRoot);\n\n\tconst stat = await fs.stat(realTargetPath);\n\tif (stat.isDirectory()) {\n\t\treturn buildDirectoryResource(url.href, realTargetPath);\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`memory:// URL must resolve to a file or directory: ${url.href}`);\n\t}\n\n\tconst content = await Bun.file(realTargetPath).text();\n\tconst contentType: InternalResource[\"contentType\"] = isMarkdownPath(realTargetPath) ? \"text/markdown\" : \"text/plain\";\n\n\treturn {\n\t\turl: url.href,\n\t\tcontent,\n\t\tcontentType,\n\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\tsourcePath: realTargetPath,\n\t\tnotes: [],\n\t};\n}\n\n/**\n * Snapshot of live mnemopi session states, deduplicated. A mnemopi backend\n * always keeps its state on the {@link AgentSession} it was initialised for;","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/memory-protocol.ts#L167-L203","documentation":"tryResolveInRoot stats the realpath'd target under the memory root and returns a directory listing for directories or file content for regular files. Anything else — symlinks to non-regular targets are already realpath'd, so this is typically devices, sockets, or FIFOs that ended up inside the memory root — cannot be served as a resource, so it throws.","triggerScenarios":"Resolving a memory://root/... URL whose target path exists but is neither a directory nor a regular file (e.g. a unix socket or named pipe created inside the memory root directory), after fs.stat succeeds in tryResolveInRoot.","commonSituations":"Unusual files placed in .omp memory roots by other tooling (sockets from dev servers, FIFOs); virtual filesystem mounts where the target is a device node.","solutions":["Inspect the target with ls -la to see what the non-file, non-directory entry is and remove or relocate it out of the memory root.","Point the memory:// URL at an actual markdown/text file or directory inside the memory root.","If a tool keeps creating special files in the memory root, move the memory root or stop that tool from writing there."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const st = await fs.stat(targetPath);\nif (!st.isFile() && !st.isDirectory()) throw new Error(`Unsupported target type: ${targetPath}`);","typeGuard":null,"tryCatchPattern":"try {\n  const res = await handler.resolve(url, ctx);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"must resolve to a file or directory\")) {\n    // inspect/remove the special file in the memory root\n  } else throw err;\n}","preventionTips":["Keep sockets/FIFOs/device nodes out of the memory root directory.","Point memory:// URLs at regular markdown/text files or directories.","Audit memory roots if third-party tools write there."],"tags":["filesystem","memory-protocol","stat"],"backgroundTag":"not-a-regular-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}