{"record":{"id":"75af9ec2deaf86f3","repo":"thedotmack/claude-mem","slug":"file-path-is-required","errorCode":null,"errorMessage":"file_path is required","messagePattern":"file_path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/smart-file-read/workspace-path.ts","lineNumber":30,"sourceCode":"\n/**\n * Resolve a caller-supplied path and refuse anything that escapes the workspace.\n *\n * smart_unfold / smart_outline / smart_search used to `resolve()` the argument\n * with no containment check, so an MCP call could read ~/.ssh/id_rsa (and any\n * other file the OS user can read). path.resolve() is lexical only — it does\n * not follow symlinks — so both sides are realpath'd before the comparison.\n * Missing targets fall back to the lexical path so the caller still gets a\n * natural ENOENT, but only after the lexical path itself is known not to escape.\n *\n * @see thedotmack/claude-mem#3861\n */\nexport async function resolveWithinWorkspace(\n  filePath: string,\n  workspaceCwd: string = process.cwd(),\n): Promise<string> {\n  if (typeof filePath !== 'string' || filePath.trim().length === 0) {\n    throw new Error('file_path is required');\n  }\n\n  const root = await realpath(resolve(workspaceCwd));\n  const lexicallyResolved = resolve(root, expandLeadingTilde(filePath.trim()));\n  let resolved: string;\n  try {\n    resolved = await realpath(lexicallyResolved);\n  } catch {\n    resolved = lexicallyResolved;\n  }\n\n  if (resolved !== root && !resolved.startsWith(root + sep)) {\n    throw new Error(\n      `Access denied: \"${filePath}\" resolves outside the workspace (${root}). ` +\n      'MCP file tools can only read files within the current project.',\n    );\n  }\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/smart-file-read/workspace-path.ts#L12-L48","documentation":"resolveWithinWorkspace resolves a requested file path safely within the workspace root for MCP smart-file-read tools. It throws 'file_path is required' when the filePath argument is missing, not a string, or empty/whitespace-only.","triggerScenarios":"An MCP file tool call passes file_path as empty string, whitespace, null/undefined, or a non-string value (e.g. a number or object) into resolveWithinWorkspace.","commonSituations":"Client omits the file_path parameter in the tool call; a template variable failed to interpolate and left an empty string; JSON schema on the client side does not enforce the parameter.","solutions":["Supply a non-empty file_path string in the MCP tool call, e.g. file_path: \"src/index.ts\".","Fix the client code so the parameter is actually populated (check for failed interpolation or undefined variables).","Validate the argument client-side before invoking: typeof p === 'string' && p.trim().length > 0."],"exampleFix":"// before\nawait read({ file_path: opts.path ?? '' });\n// after\nif (!opts.path || !opts.path.trim()) throw new Error('file_path is required');\nawait read({ file_path: opts.path });","handlingStrategy":"validation","validationCode":"function hasFilePath(args: unknown): args is { file_path: string } {\n  return typeof args === 'object' && args !== null &&\n    typeof (args as any).file_path === 'string' && (args as any).file_path.trim().length > 0;\n}","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  const resolved = await resolveWithinWorkspace(args.file_path);\n} catch (err) {\n  if (String(err) === 'Error: file_path is required') return mcpError(400, 'file_path parameter is required');\n  throw err;\n}","preventionTips":["Enforce file_path as required in the MCP tool's JSON schema.","Check for empty template variables before dispatching tool calls.","Trim and validate user input at the client boundary."],"tags":["validation","arguments","mcp"],"backgroundTag":"missing-required-argument","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}