siyuan-note/siyuan · error

path escapes workspace

Error message

path escapes workspace: %s

What it means

absPath resolves a user-supplied relative path against the SiYuan workspace directory and refuses paths that would resolve outside it. It cleans the path, joins it onto util.WorkspaceDir, and verifies containment with gulu.File.IsSubPath; on violation it returns this error naming the offending relative path. This is a path-traversal defense protecting workspace files.

Solutions

  1. Use paths relative to the workspace root without `..` escape segments (e.g. `notebooks/<box>/doc.sy`).
  2. Resolve the intended file inside the workspace and re-run the command.
  3. If you need a file outside the workspace, copy it in first or use the appropriate non-workspace-scoped tooling.
  4. Sanitize/normalize user input in scripts before passing it to the CLI.

Example fix

// before
siyuan file list ../../secrets
// error: path escapes workspace: ../../secrets

// after
siyuan file list notebooks/20240101120000-abc1234/20240501120000-xyz9876.sy
Defensive patterns

Strategy: validation

Validate before calling

// Go caller-side check before invoking CLI file commands
rel := filepath.ToSlash(filepath.Clean(userPath))
if strings.HasPrefix(rel, "../") || rel == ".." || filepath.IsAbs(userPath) {
    return fmt.Errorf("refusing non-workspace-relative path: %s", rel)
}

Type guard

func safeWorkspaceRel(p string) (string, bool) {
    cleaned := filepath.ToSlash(filepath.Clean(p))
    if filepath.IsAbs(p) || cleaned == ".." || strings.HasPrefix(cleaned, "../") {
        return "", false
    }
    return cleaned, true
}

Try / catch

abs, err := absPath(rel)
if err != nil {
    if strings.Contains(err.Error(), "path escapes workspace") {
        return fmt.Errorf("refusing path outside workspace: %s", rel)
    }
    return err
}

Prevention

When it happens

Trigger: Passing a path containing `..` segments (e.g. `../../etc/passwd`) or an absolute path outside the workspace to CLI file commands such as `siyuan file list <path>` / read / write.

Common situations: Scripting file operations with paths built from external input; typos with extra `..`; attempting to read files adjacent to the workspace; automated tools given paths in the wrong root.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/3c24a07237f8e4d9. Report an issue: GitHub.

Appendix: source

Thrown at kernel/cli/cmd/file.go:44

	"text/tabwriter"

	"github.com/88250/gulu"
	"github.com/siyuan-note/siyuan/kernel/model"
	"github.com/siyuan-note/siyuan/kernel/util"

	"github.com/spf13/cobra"
)

var fileCmd = &cobra.Command{
	Use:   "file",
	Short: "Workspace file operations",
}

func absPath(rel string) (string, error) {
	rel = filepath.Clean(strings.ReplaceAll(rel, "/", string(os.PathSeparator)))
	abs := filepath.Join(util.WorkspaceDir, rel)
	if !gulu.File.IsSubPath(util.WorkspaceDir, abs) {
		return "", fmt.Errorf("path escapes workspace: %s", rel)
	}
	if boxID := model.EncryptedRawPathBoxID(abs); boxID != "" {
		return "", fmt.Errorf("path belongs to encrypted notebook [%s]: %s", boxID, rel)
	}
	return abs, nil
}

var fileListCmd = &cobra.Command{
	Use:   "list <path>",
	Short: "List directory contents",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		dir, err := absPath(args[0])
		if err != nil {
			return err
		}
		entries, err := os.ReadDir(dir)
		if err != nil {

View on GitHub (pinned to 9f775e8a12)