{"record":{"id":"d293629dbdc9fa9b","repo":"amruthpillai/reactive-resume","slug":"invalid-resume-uri-expected-format-resume-id","errorCode":null,"errorMessage":"Invalid resume URI — expected format: resume://{id}","messagePattern":"Invalid resume URI — expected format: resume://(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp/src/resources.ts","lineNumber":28,"sourceCode":"\t// Template resource: read resume JSON by ID. Discovery is via list tool (tools), not resources/list.\n\tconst resumeTemplate = new ResourceTemplate(\"resume://{id}\", { list: undefined });\n\n\tserver.registerResource(\n\t\t\"resume\",\n\t\tresumeTemplate,\n\t\t{\n\t\t\ttitle: \"Resume Data\",\n\t\t\tmimeType: \"application/json\",\n\t\t\tdescription: [\n\t\t\t\t\"Full resume data as JSON, including basics, summary, sections, custom sections, and metadata.\",\n\t\t\t\t`Discover resume IDs with the \\`${T.listResumes}\\` tool, then read \\`resume://{id}\\` or use \\`${T.getResume}\\`.`,\n\t\t\t\t\"Appears in `resources/templates/list`; not enumerated in `resources/list`.\",\n\t\t\t\t\"Embedded as context in MCP prompts (build_resume, improve_resume, etc.).\",\n\t\t\t].join(\" \"),\n\t\t},\n\t\tasync (uri: URL) => {\n\t\t\tconst id = uri.href.replace(/^resume:\\/\\//, \"\");\n\t\t\tif (!id) throw new Error(\"Invalid resume URI — expected format: resume://{id}\");\n\n\t\t\tconst resume = await client.resume.getById({ id });\n\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: \"application/json\" as const,\n\t\t\t\t\t\ttext: JSON.stringify(resume.data, null, 2),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t},\n\t);\n\n\t// ── Resource: resume://_meta/schema ───────────────────────────\n\t// Static resource containing the JSON Schema for resume data.\n\t// LLMs should reference this when generating JSON Patch operations","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/mcp/src/resources.ts#L10-L46","documentation":"MCP resource handler for the 'resume://' resource template strips the resume:// scheme and throws if the remaining id is empty. The resource URI must be resume://{id}; calling resume:// or resume:/// with no id segment is invalid. Generic Error surfaced through the MCP resource read.","triggerScenarios":"An MCP client reads resources/list and then attempts to read a URI constructed without an id; a client forms resume:// (no id) by mistake; the id was stripped/URL-decoded to empty.","commonSituations":"Programmatic client that builds URIs from a template variable which was undefined; a malformed URI copy-paste; an automation that iterates an empty id list.","solutions":["Always read the resume resource with a concrete id obtained from the listResumes tool: resume://<id>.","Prefer the getResume tool over the resource URI when you already have the id.","Validate the URI matches /^resume:\\/\\/[\\w-]+$/ before issuing resources/read."],"exampleFix":"// before: empty id\nawait client.resources.read('resume://');\n// after: concrete id from listResumes\nconst { ids } = await client.tools.call('listResumes', {});\nawait client.resources.read(`resume://${ids[0]}`);","handlingStrategy":"validation","validationCode":"const re = /^resume:\\/\\/[\\w-]+$/;\nif (!re.test(uri)) throw new TypeError(`Expected resume://{id}, got: ${uri}`);","typeGuard":"function isResumeUri(uri: string): uri is `resume://${string}` {\n  return /^resume:\\/\\/[\\w-]+$/.test(uri);\n}","tryCatchPattern":null,"preventionTips":["Always source the id from the listResumes tool.","Prefer the getResume tool when you already have the id.","Validate the URI shape before resources/read."],"tags":["mcp","resources","uri","validation"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}