siyuan-note/siyuan · error
resource path [ ] is not in workspace
Error message
resource path [%s] is not in workspace
What it means
ExportResources validates that every requested resource path resolves to an absolute path inside the workspace. If filepath.Join(util.WorkspaceDir, resourcePath) escapes the workspace, it logs and returns 'resource path [%s] is not in workspace' — a workspace-escape guard against arbitrary file reads.
Solutions
- Pass resource paths relative to the workspace data dir (e.g. 'assets/foo.png', 'notebooks/...') without '..' segments
- Sanitize input with filepath.Clean and reject paths containing '..' before calling
- Verify with util.IsAbsPathInWorkspace(filepath.Join(util.WorkspaceDir, p)) in the caller
Example fix
// before
model.ExportResources([]string{"/etc/passwd"})
// after
p := filepath.Clean("assets/img.png") // relative, inside workspace
model.ExportResources([]string{p}) Defensive patterns
Strategy: validation
Validate before calling
func safeResource(p string) bool {
full := filepath.Join(util.WorkspaceDir, p)
return util.IsAbsPathInWorkspace(full) && !strings.Contains(filepath.Clean(p), "..")
} Type guard
func inWorkspace(p string) bool {
return util.IsAbsPathInWorkspace(filepath.Join(util.WorkspaceDir, p))
} Try / catch
if _, err := model.ExportResources(paths); err != nil {
// path escape: filter paths with safeResource and retry
} Prevention
- Only pass paths relative to the workspace data dir
- Reject '..' segments and absolute paths from user input
- Resolve and re-check paths after any workspace relocation
When it happens
Trigger: Passing relative paths that traverse outside the workspace (e.g. '../..' segments), absolute-ish resource paths, or paths from another workspace into ExportResources.
Common situations: Scripting exports from user-supplied paths; callers concatenating external paths; workspace relocated so cached relative paths no longer match.
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
- access to sensitive workspace file is forbidden
- archive entry escapes destination
- asset path is sensitive
- export path is outside export directory
- export path is outside export directory
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/2bd0cdf32a3ea42c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/export.go:881
zipFilePath := filepath.Join(exportBasePath, exportID+"-"+zipFileName)
if err = os.MkdirAll(exportFolderPath, 0755); err != nil {
logging.LogErrorf("create export temp folder failed: %s", err)
return
}
defer func() {
os.RemoveAll(exportFolderPath)
if err != nil {
os.Remove(zipFilePath)
os.Remove(zipFilePath + ".partial")
}
}()
// 将需要导出的文件/文件夹复制到临时文件夹
for _, resourcePath := range resourcePaths {
resourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath) // 资源完整路径
if !util.IsAbsPathInWorkspace(resourceFullPath) {
logging.LogErrorf("resource path [%s] is not in workspace", resourceFullPath)
err = errors.New("resource path [" + resourcePath + "] is not in workspace")
return
}
resourceBaseName := filepath.Base(resourceFullPath) // 资源名称
resourceCopyPath := filepath.Join(exportFolderPath, resourceBaseName) // 资源副本完整路径
if err = copyExportResource(resourceFullPath, resourceCopyPath); err != nil {
logging.LogErrorf("copy resource will be exported from [%s] to [%s] failed: %s", resourcePath, resourceCopyPath, err)
err = fmt.Errorf(Conf.Language(14), err.Error())
return
}
}
zipPartialPath := zipFilePath + ".partial"
zip, err := gulu.Zip.Create(zipPartialPath)
if err != nil {
logging.LogErrorf("create export zip [%s] failed: %s", zipFilePath, err)
return
}View on GitHub (pinned to 9f775e8a12)