{"record":{"id":"1dd5e6ef971c0a27","repo":"vxcontrol/pentagi","slug":"flowfiles-notfound","errorCode":"FlowFiles.NotFound","errorMessage":"no accessible paths found","messagePattern":"no accessible paths found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"backend/pkg/server/services/flow_files.go","lineNumber":529,"sourceCode":"\t\t\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"error reading cached flow file\")\n\t\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\t// Never serve symlinks — could point outside the flow data directory.\n\t\tif info.Mode()&os.ModeSymlink != 0 {\n\t\t\tresponse.Error(c, response.ErrFlowFilesNotFound, fmt.Errorf(\"'%s' not found in local cache\", reqPath))\n\t\t\treturn\n\t\t}\n\t\tif !info.IsDir() && !info.Mode().IsRegular() {\n\t\t\tresponse.Error(c, response.ErrFlowFilesNotFound, fmt.Errorf(\"'%s' not found in local cache\", reqPath))\n\t\t\treturn\n\t\t}\n\t\tentries = append(entries, resolvedEntry{reqPath: reqPath, localPath: localPath, info: info})\n\t}\n\n\tif len(entries) == 0 {\n\t\tresponse.Error(c, response.ErrFlowFilesNotFound, errors.New(\"no accessible paths found\"))\n\t\treturn\n\t}\n\n\t// Single regular file → serve as a direct attachment.\n\t// We open the file explicitly and use DataFromReader with a known Content-Length\n\t// so that Gin's response writer emits a proper Content-Length header instead of\n\t// relying on http.ServeFile, which conflicts with Gin's middleware-set headers\n\t// and can produce content-length: 0 in certain request contexts.\n\tif len(entries) == 1 && entries[0].info.Mode().IsRegular() {\n\t\te := entries[0]\n\t\tf, err := os.Open(e.localPath)\n\t\tif err != nil {\n\t\t\tlogger.FromContext(c).WithError(err).WithField(\"flow_id\", flowID).Error(\"error opening flow file for download\")\n\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\treturn\n\t\t}\n\t\tdefer f.Close()\n","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/flow_files.go#L511-L547","documentation":"DownloadFlowFile resolves each requested path (inside the flow's working directory / container volume) and stats it; only entries that exist and are accessible are collected. If every requested path failed resolution — nonexistent, permission-denied, or filtered — the handler returns FlowFiles.NotFound ('no accessible paths found') instead of a partial download.","triggerScenarios":"GET /flows/{flow_id}/files/download?path=<p> where <p> (and all paths[] entries) do not resolve to an existing, readable file or directory — wrong relative path, file already deleted, or path outside the allowed root rejected during resolution.","commonSituations":"Downloading before the flow's container has produced the file; path typos or absolute vs relative path confusion; files removed between listing and download; case-sensitive path mismatches.","solutions":["List available files first (GET /flows/<id>/files) and download an exact returned path","Use a flow-relative path rather than a host or container absolute path","Verify the file still exists and the flow/container is still running","Check for typos and case sensitivity in the path"],"exampleFix":"// before\nGET /api/v1/flows/42/files/download?path=/root/report.txt\n// after\nGET /api/v1/flows/42/files/download?path=report.txt  (flow-relative, confirmed via list endpoint)","handlingStrategy":"try-catch","validationCode":"// pre-check existence via the list endpoint\nconst listed = await api.get(`/flows/${flowId}/files`);\nconst available = new Set(listed.data.files.map(f => f.path));\nconst valid = paths.filter(p => available.has(p));\nif (valid.length === 0) throw new Error('none of the requested paths exist for this flow');","typeGuard":null,"tryCatchPattern":"try {\n  const res = await api.get(`/flows/${flowId}/files/download`, { params, responseType: 'blob' });\n} catch (e) {\n  if (e.response?.status === 404) {\n    // refresh the file listing and prompt the user to reselect\n    await refreshFileList();\n    notify('Requested files are no longer accessible');\n    return;\n  }\n  throw e;\n}","preventionTips":["Always list files via the files endpoint and download from that list","Treat download URLs as ephemeral — files may vanish as the flow progresses","Use flow-relative paths exactly as returned by the API","Refresh listings before batch downloads of long-lived selections"],"tags":["api","not-found","file-download","path-resolution"],"backgroundTag":"file-not-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}