{"record":{"id":"9e826aec9d59640e","repo":"vxcontrol/pentagi","slug":"path-must-be-relative-no-leading","errorCode":null,"errorMessage":"path must be relative (no leading /)","messagePattern":"path must be relative \\(no leading /\\)","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/pkg/flowfiles/files.go","lineNumber":103,"sourceCode":"func FlowUploadsDir(dataDir string, flowID uint64) string {\n\treturn filepath.Join(FlowDataDir(dataDir, flowID), UploadsDirName)\n}\n\nfunc FlowContainerDir(dataDir string, flowID uint64) string {\n\treturn filepath.Join(FlowDataDir(dataDir, flowID), ContainerDirName)\n}\n\nfunc FlowResourcesDir(dataDir string, flowID uint64) string {\n\treturn filepath.Join(FlowDataDir(dataDir, flowID), ResourcesDirName)\n}\n\nfunc ResolveCachedPath(dataDir string, flowID uint64, reqPath string) (string, error) {\n\tif strings.TrimSpace(reqPath) == \"\" {\n\t\treturn \"\", errors.New(\"path query parameter is required\")\n\t}\n\n\tcleaned := filepath.Clean(filepath.FromSlash(strings.ReplaceAll(reqPath, \"\\\\\", \"/\")))\n\tif filepath.IsAbs(cleaned) {\n\t\treturn \"\", fmt.Errorf(\"path must be relative (no leading /)\")\n\t}\n\n\tparts := strings.SplitN(cleaned, string(filepath.Separator), 2)\n\tif parts[0] != UploadsDirName && parts[0] != ContainerDirName && parts[0] != ResourcesDirName {\n\t\treturn \"\", fmt.Errorf(\"path must start with '%s', '%s', or '%s'\", UploadsDirName, ContainerDirName, ResourcesDirName)\n\t}\n\n\tflowDataDir := FlowDataDir(dataDir, flowID)\n\tabsPath := filepath.Join(flowDataDir, cleaned)\n\tif !IsWithinDir(absPath, flowDataDir) {\n\t\treturn \"\", fmt.Errorf(\"path escapes the flow data directory\")\n\t}\n\n\treturn absPath, nil\n}\n\nfunc SanitizeFileName(fileName string) (string, error) {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/flowfiles/files.go#L85-L121","documentation":"ResolveCachedPath normalizes the request path (backslashes to slashes, filepath.Clean) and rejects absolute paths. Absolute paths could escape the per-flow sandbox directory, so any path starting with '/' (or a Windows drive after cleaning) is rejected. Callers must supply a path relative to the flow data directory.","triggerScenarios":"Calling ResolveCachedPath / AddResourceFromFlow with a path like '/uploads/x.png', an absolute filesystem path pasted by the user, or a path constructed with a leading separator on either OS.","commonSituations":"Client concatenates a server-provided absolute path with the endpoint base URL; a user pastes an absolute container path into a UI field; Windows-style paths like 'C:\\\\...' that Clean turns absolute.","solutions":["Strip the leading '/' and send only the relative portion, e.g. 'uploads/report.pdf' instead of '/uploads/report.pdf'","If you have an absolute path returned by another API, compute the relative portion (strip the flow data dir prefix) before calling","On Windows-style input, remove drive letters and convert backslashes to forward slashes"],"exampleFix":"// before\nconst p = '/uploads/report.pdf';\n// after\nconst p = 'uploads/report.pdf';","handlingStrategy":"validation","validationCode":"if (reqPath.startsWith('/') || /^[a-zA-Z]:/.test(reqPath)) throw new Error('path must be relative');","typeGuard":"const isRelativePath = (p: string): boolean => !p.startsWith('/') && !/^[a-zA-Z]:[\\\\/]/.test(p);","tryCatchPattern":"try {\n  const resolved = await api.resolveCachedPath(flowID, reqPath);\n} catch (e) {\n  if (e.message.includes('path must be relative')) {\n    reqPath = reqPath.replace(/^([a-zA-Z]:)?[\\\\/]+/, '');\n    // retry with the stripped relative path\n  }\n}","preventionTips":["Store and transmit only flow-relative paths, never absolute ones","Strip leading slashes/drive letters at the boundary where paths enter your client code","Never paste absolute container paths into path fields; use the server-returned relative path"],"tags":["input-validation","path-traversal","security"],"backgroundTag":"invalid-path-format","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}