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

  1. Prefix the path with one of: uploads/, container/, resources/
  2. Take paths directly from the List endpoint output, which always uses these roots
  3. Fix client constants that reference old/renamed directory names
  4. 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

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


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/1a662173a2967dab. Report an issue: GitHub.