vxcontrol/pentagi · error

path must be relative (no leading /)

Error message

path must be relative (no leading /)

What it means

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.

Source

Thrown at backend/pkg/flowfiles/files.go:103

func FlowUploadsDir(dataDir string, flowID uint64) string {
	return filepath.Join(FlowDataDir(dataDir, flowID), UploadsDirName)
}

func FlowContainerDir(dataDir string, flowID uint64) string {
	return filepath.Join(FlowDataDir(dataDir, flowID), ContainerDirName)
}

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) {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Strip the leading '/' and send only the relative portion, e.g. 'uploads/report.pdf' instead of '/uploads/report.pdf'
  2. If you have an absolute path returned by another API, compute the relative portion (strip the flow data dir prefix) before calling
  3. On Windows-style input, remove drive letters and convert backslashes to forward slashes

Example fix

// before
const p = '/uploads/report.pdf';
// after
const p = 'uploads/report.pdf';
Defensive patterns

Strategy: validation

Validate before calling

if (reqPath.startsWith('/') || /^[a-zA-Z]:/.test(reqPath)) throw new Error('path must be relative');

Type guard

const isRelativePath = (p: string): boolean => !p.startsWith('/') && !/^[a-zA-Z]:[\\/]/.test(p);

Try / catch

try {
  const resolved = await api.resolveCachedPath(flowID, reqPath);
} catch (e) {
  if (e.message.includes('path must be relative')) {
    reqPath = reqPath.replace(/^([a-zA-Z]:)?[\\/]+/, '');
    // retry with the stripped relative path
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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