vxcontrol/pentagi · warning
path must start with '%s', '%s', or '%s'
Error message
path must start with '%s', '%s', or '%s'
What it means
ResolveCachedPath requires the first path component to be one of the three known cache roots: 'uploads', 'container', or 'resources'. Any other prefix cannot be resolved to a flow cache location and is rejected with this formatted message naming the three allowed roots.
Source
Thrown at backend/pkg/flowfiles/files.go:109
}
func FlowResourcesDir(dataDir string, flowID uint64) string {
return filepath.Join(FlowDataDir(dataDir, flowID), ResourcesDirName)
}
func ResolveCachedPath(dataDir string, flowID uint64, reqPath string) (string, error) {
if strings.TrimSpace(reqPath) == "" {
return "", errors.New("path query parameter is required")
}
cleaned := filepath.Clean(filepath.FromSlash(strings.ReplaceAll(reqPath, "\\", "/")))
if filepath.IsAbs(cleaned) {
return "", fmt.Errorf("path must be relative (no leading /)")
}
parts := strings.SplitN(cleaned, string(filepath.Separator), 2)
if parts[0] != UploadsDirName && parts[0] != ContainerDirName && parts[0] != ResourcesDirName {
return "", fmt.Errorf("path must start with '%s', '%s', or '%s'", UploadsDirName, ContainerDirName, ResourcesDirName)
}
flowDataDir := FlowDataDir(dataDir, flowID)
absPath := filepath.Join(flowDataDir, cleaned)
if !IsWithinDir(absPath, flowDataDir) {
return "", fmt.Errorf("path escapes the flow data directory")
}
return absPath, nil
}
func SanitizeFileName(fileName string) (string, error) {
trimmedName := strings.TrimSpace(fileName)
if trimmedName == "" {
return "", fmt.Errorf("file name is required")
}
normalizedName := strings.ReplaceAll(trimmedName, "\\", "/")View on GitHub (pinned to ea665308ba)
Solutions
- Prefix the path with one of: uploads/, container/, resources/
- Take paths directly from the List endpoint output, which always uses these roots
- Fix client constants that reference old/renamed directory names
- Check for accidental './' or empty leading segments in the constructed path
Example fix
// before resolveCachedPath(flowID, "files/report.pdf") // after resolveCachedPath(flowID, "resources/report.pdf")
Defensive patterns
Strategy: validation
Validate before calling
func hasValidRoot(p string) bool {
first := strings.SplitN(strings.ReplaceAll(p, "\\", "/"), "/", 2)[0]
return first == "uploads" || first == "container" || first == "resources"
} Type guard
func isBadRootError(err error) bool { return err != nil && strings.Contains(err.Error(), "path must start with") } Try / catch
abs, err := flowfiles.ResolveCachedPath(dataDir, flowID, reqPath)
if err != nil {
if strings.Contains(err.Error(), "path must start with") {
http.Error(w, "path must start with uploads/, container/, or resources/", http.StatusBadRequest)
return
}
return err
} Prevention
- Keep a single shared constant for allowed roots in frontend and backend
- Prefer consuming List output over constructing paths manually
- Update clients whenever cache root names change
- Validate the first path segment client-side before calling the API
When it happens
Trigger: Passing a path whose first segment is anything else, e.g. 'tmp/file.txt', 'flow-data/x', or an empty-ish segment like './file.txt' after Clean.
Common situations: API consumers inventing their own prefixes; frontend hardcoding a legacy directory name that was renamed; concatenating flow IDs or other prefixes onto the path.
Related errors
- path escapes the flow data directory
- path must be relative (no leading /)
- Internal
- Agentlogs.InvalidRequest
- Token.InvalidRequest
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/1a662173a2967dab.
Report an issue: GitHub.