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
- Use paths relative to the workspace root without `..` escape segments (e.g. `notebooks/<box>/doc.sy`).
- Resolve the intended file inside the workspace and re-run the command.
- If you need a file outside the workspace, copy it in first or use the appropriate non-workspace-scoped tooling.
- 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
- Always pass workspace-relative paths without .. segments
- Sanitize externally supplied paths before passing to the CLI
- Never attempt to reach files outside the workspace through file commands
- Treat this error as a security signal, not a nuisance
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
- path escapes templates dir
- archive entry resolves outside destination
- asset escapes its directory
- asset path escapes data directory
- asset path escapes data directory
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)