wavetermdev/waveterm · error

invalid source path: %w

Error message

invalid source path: %w

What it means

After appId validation, RenameAppFile resolves the SOURCE file name via validateAndResolveFilePath and wraps any failure as 'invalid source path' (waveappstore.go:388-391). The resolver rejects absolute paths, '..' path traversal, and any path that escapes the app directory — this prevents renaming files outside ~/waveapps/<ns>/<app>.

Source

Thrown at pkg/waveappstore/waveappstore.go:390

		return nil, err
	}

	return fileutil.ReplaceInFilePartial(filePath, edits)
}

func RenameAppFile(appId string, fromFileName string, toFileName string) error {
	if err := ValidateAppId(appId); err != nil {
		return fmt.Errorf("invalid appId: %w", err)
	}

	appDir, err := GetAppDir(appId)
	if err != nil {
		return err
	}

	fromPath, err := validateAndResolveFilePath(appDir, fromFileName)
	if err != nil {
		return fmt.Errorf("invalid source path: %w", err)
	}

	toPath, err := validateAndResolveFilePath(appDir, toFileName)
	if err != nil {
		return fmt.Errorf("invalid destination path: %w", err)
	}

	if err := os.MkdirAll(filepath.Dir(toPath), 0755); err != nil {
		return fmt.Errorf("failed to create destination directory: %w", err)
	}

	if err := os.Rename(fromPath, toPath); err != nil {
		return fmt.Errorf("failed to rename file: %w", err)
	}

	return nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass fromFileName as a plain relative path inside the app, e.g. 'old.txt' or 'sub/dir/file.go'.
  2. Strip any leading '/' and resolve '..' segments before calling; reject them if the result escapes the app.
  3. If the goal is cross-app or cross-namespace moves, do ReadAppFile + WriteAppFile + DeleteAppFile instead of '..' traversal.
  4. Check the wrapped sub-error: it names the exact rule violated (absolute path / path traversal / escapes app directory).

Example fix

// before
waveappstore.RenameAppFile(appId, "/home/user/waveapps/local/app/old.txt", "new.txt") // absolute rejected

// after
err := waveappstore.RenameAppFile(appId, "old.txt", "new.txt") // relative to app dir
if err != nil && strings.Contains(err.Error(), "invalid source path") {
    return fmt.Errorf("source must be a relative path inside the app: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func safeRelName(name string) (string, error) {
    if filepath.IsAbs(name) {
        return "", fmt.Errorf("must be relative: %s", name)
    }
    clean := filepath.Clean(name)
    if strings.HasPrefix(clean, "..") || strings.Contains(clean, string(filepath.Separator)+"..") {
        return "", fmt.Errorf("traversal not allowed: %s", name)
    }
    return clean, nil
}
// run on fromFileName (and toFileName) before RenameAppFile

Try / catch

err := waveappstore.RenameAppFile(appId, fromName, toName)
if err != nil {
    var kind string
    switch {
    case strings.Contains(err.Error(), "invalid source path"):
        kind = "source"
    case strings.Contains(err.Error(), "invalid destination path"):
        kind = "destination"
    }
    return fmt.Errorf("rename rejected (%s): %w", kind, err)
}

Prevention

When it happens

Trigger: Calling RenameAppFile / RenameAppFileCommand with fromFileName that is absolute ('/etc/passwd'), contains '..' segments ('../shared.txt'), cleans to escape the app dir, or otherwise fails validateAndResolveFilePath.

Common situations: Frontends forwarding user-typed paths that include leading '/' or '../'; code joining a base dir into fileName before calling (producing an absolute path); attempts to move files between apps by using '../otherapp/file' instead of two rename operations.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/2c1178cc3d1daab5. Report an issue: GitHub.