{"record":{"id":"6942e89a84ead204","repo":"dmtrKovalenko/fff","slug":"path-constraint-must-be-relative-to-the-workspace","errorCode":null,"errorMessage":"Path constraint must be relative to the workspace: ${pathConstraint}","messagePattern":"Path constraint must be relative to the workspace: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pi-fff/src/query.ts","lineNumber":14,"sourceCode":"import path from \"node:path\";\n\nexport function normalizePathConstraint(\n  pathConstraint: string,\n  cwd = process.cwd(),\n): string | null {\n  let trimmed = pathConstraint.trim();\n  if (!trimmed) return trimmed;\n\n  if (path.isAbsolute(trimmed)) {\n    const relative = path.relative(cwd, trimmed).replaceAll(path.sep, \"/\");\n    if (relative === \"\") return null;\n    if (relative.startsWith(\"../\") || relative === \"..\" || path.isAbsolute(relative)) {\n      throw new Error(\n        `Path constraint must be relative to the workspace: ${pathConstraint}`,\n      );\n    }\n    trimmed = relative;\n  }\n\n  if (trimmed === \".\" || trimmed === \"./\") return null;\n  // Strip a leading `./` so `./**/*.rs` and `**/*.rs` behave identically.\n  if (trimmed.startsWith(\"./\")) trimmed = trimmed.slice(2);\n\n  // wif we left with the ** it means anything so treat it as a cwd path\n  if (trimmed === \"**\" || trimmed === \"**/\" || trimmed === \"**/*\") return null;\n\n  // FFF's glob matcher can treat a hidden directory root glob such as\n  // `.agents/**` as empty, while the tool contract says this means \"inside\n  // this directory\". Collapse simple trailing recursive directory globs to the\n  // directory-prefix constraint understood by the parser. Keep real file globs\n  // such as `src/**/*.ts` unchanged.","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/packages/pi-fff/src/query.ts#L1-L32","documentation":"normalizePathConstraint validates user-supplied path constraints in query.ts. If the value is an absolute path that resolves outside the workspace (relative path escapes via ../) it is rejected. The library only accepts path constraints that stay inside the current working directory.","triggerScenarios":"Calling a query/search API with pathConstraint set to an absolute path outside cwd (e.g. '/etc' or '/home/user/other-project'), or a path that normalizes to a parent of the workspace, or an absolute path equal to cwd's parent.","commonSituations":"Passing an absolute file path copied from an editor instead of a relative one; running the tool from a different cwd than expected so a previously-valid relative path now escapes; passing a sibling directory path like '/repo/../other' .","solutions":["Use a path relative to the workspace root, e.g. 'src/lib' instead of '/abs/path/src/lib'.","Check the process cwd is the intended workspace root before querying.","Strip or rebase the absolute path against cwd yourself and verify it does not start with '../' before passing it.","If you intended to search another project, change the workspace/cwd rather than passing an external path."],"exampleFix":"// before\nquery({ pathConstraint: '/home/me/project/src/util.ts' })\n// after\nquery({ pathConstraint: 'src/util.ts' })","handlingStrategy":"validation","validationCode":"function isRelativeInsideWorkspace(p, cwd) {\n  if (typeof p !== 'string' || !p.trim()) return false;\n  if (!path.isAbsolute(p)) return true;\n  const rel = path.relative(cwd, p).replaceAll(path.sep, '/');\n  return rel !== '' && !rel.startsWith('../') && rel !== '..' && !path.isAbsolute(rel);\n}","typeGuard":"const isSafePathConstraint = (p) => typeof p === 'string' && p.trim().length > 0 && !p.startsWith('/');","tryCatchPattern":"try {\n  await query({ pathConstraint: raw });\n} catch (e) {\n  if (String(e.message).includes('Path constraint must be relative')) {\n    return query({ pathConstraint: path.relative(cwd, raw) });\n  }\n  throw e;\n}","preventionTips":["Always pass workspace-relative paths.","Rebase absolute paths against cwd before querying.","Log cwd at startup to catch wrong-working-directory issues.","Reject inputs starting with '../' at your API boundary."],"tags":["path","validation","node"],"backgroundTag":"path-traversal-blocked","analyzedSha":"7f8537e70f0ea1210f9acbbfc4640141105cdc78","analyzedAt":"2026-09-10T07:26:53.407Z","contentChangedAt":"2026-09-10T07:26:53.407Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}