siyuan-note/siyuan · warning
export path is outside export directory
Error message
export path is outside export directory
What it means
For non-managed export names, the artifact path is built as `<TempDir>/export/<fileName>` and then verified with gulu.File.IsSubPath. This error means the resulting absolute path is not inside the export directory — a defensive containment check that also catches symlink/normalization surprises.
Source
Thrown at kernel/model/encrypted_export.go:219
ReleaseEncryptedBoxOperation(boxID)
}
}()
_, artifact, resolved = ResolveManagedEncryptedExport(fileName)
if !resolved {
return nil, errors.New("managed export is unavailable")
}
if _, dekErr := GetDEKIfUnlocked(boxID); dekErr != nil {
return nil, dekErr
}
lease, err = registerMobileExportLease(boxID, artifact, filepath.Base(fileName), "")
if err == nil {
release = false
}
return
}
artifact := filepath.Join(util.TempDir, "export", fileName)
if !gulu.File.IsSubPath(filepath.Join(util.TempDir, "export"), artifact) {
return nil, errors.New("export path is outside export directory")
}
return registerMobileExportLease("", artifact, filepath.Base(fileName), "")
}
if !strings.HasPrefix(exportPath, "assets/") {
return nil, errors.New("unsupported export path")
}
relativePath, boxID, parseErr := assetPathAndBox(exportPath, "")
if parseErr != nil {
return nil, parseErr
}
if boxID == "" || !IsEncryptedBox(boxID) {
artifact, resolveErr := GetAssetAbsPath(relativePath)
if resolveErr != nil {
return nil, resolveErr
}
if ensureErr := EnsureAssetLocal(artifact); ensureErr != nil {
return nil, ensureErrView on GitHub (pinned to 8641553a1f)
Solutions
- Send only simple file names without directory components to the export lease API.
- Prefer managed export paths produced by the export flow over hand-built relative paths.
- Verify the client is not double-joining paths (e.g. already prefixed with 'export/').
- If TempDir is customized/symlinked, confirm the export directory resolves inside the workspace temp tree.
Example fix
// before
{name: "sub\..\..\report.html"} // escapes TempDir/export after join
// after
{name: "report.html"} Defensive patterns
Strategy: validation
Validate before calling
// Go: verify the joined artifact stays inside the export dir before calling the API
artifact := filepath.Join(util.TempDir, "export", name)
if !gulu.File.IsSubPath(filepath.Join(util.TempDir, "export"), artifact) {
return errors.New("name escapes export directory")
} Prevention
- Send plain file names without any directory components.
- Do not pre-join client paths with 'export/' before calling the API.
- Prefer managed export paths from the export flow for encrypted notebooks.
When it happens
Trigger: AcquireExportArtifactLease called with a name that, after joining with the temp export dir and cleaning, escapes `util.TempDir/export` (e.g. crafted names with separators, or asset paths outside `assets/` handled by adjacent checks).
Common situations: Client-supplied names containing path segments that survive filepath.Clean; platform-specific separator tricks; exporting on a system where TempDir is symlinked elsewhere.
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
- invalid export path
- resource path [%s] is not in workspace
- export path is outside export directory
- Please unlock the encrypted notebook first
- import path is not sub path of import dir
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/4c91a9733060ae47.
Report an issue: GitHub.