siyuan-note/siyuan · error
exporting resources across encrypted notebook boundaries is…
Error message
exporting resources across encrypted notebook boundaries is not supported
What it means
The exporter allows batching resources from one encrypted notebook, but resources must not come from two different encrypted notebooks in a single export. Once an encrypted boxID is recorded, any subsequent resource from a different encrypted box aborts the export, since each encrypted box requires its own decryption context.
Solutions
- Split the export into one ExportResources call per encrypted notebook
- Group requested resources by boxID before calling ExportResources
- Mix encrypted resources with normal-notebook resources is also rejected — keep batches homogeneous
Example fix
// before
ExportResources([]string{"boxA/assets/a.png", "boxB/assets/b.png"}) // both encrypted
// after
ExportResources([]string{"boxA/assets/a.png"})
ExportResources([]string{"boxB/assets/b.png"}) Defensive patterns
Strategy: validation
Validate before calling
function groupByBox(paths) {
const groups = {};
for (const p of paths) {
const box = p.split('/')[0];
(groups[box] = groups[box] || []).push(p);
}
return Object.values(groups); // one ExportResources call per group
} Try / catch
try { await exportResources(paths); }
catch (e) {
if (String(e).includes('across encrypted notebook boundaries')) {
for (const g of groupByBox(paths)) await exportResources(g);
}
} Prevention
- Never batch resources from multiple encrypted notebooks in one call
- Group export requests by notebook ID before invoking the API
- Keep UI multi-select exports scoped per notebook when encryption is involved
When it happens
Trigger: ExportResources called with asset paths from two different encrypted notebooks in the same call (boxID extracted from the second path differs from the first).
Common situations: UI multi-select export across notebooks where more than one selected notebook is encrypted; plugin aggregating assets from several encrypted notebooks into one request.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- exporting encrypted and normal notebook resources together…
- exporting non-asset files from encrypted notebooks is not…
- managed export is unavailable
- 314
- 314
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0bf99ac08cb91838.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/export.go:1018
for _, resourcePath := range resourcePaths {
resourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath)
if !util.IsAbsPathInWorkspace(resourceFullPath) {
return "", errors.New("resource path [" + resourcePath + "] is not in workspace")
}
boxID := ExtractBoxIDFromAssetsPath(resourceFullPath)
if boxID == "" || !IsEncryptedBox(boxID) {
hasNormalResource = true
continue
}
assetsPath := filepath.Join(util.DataDir, boxID, "assets")
if !gulu.File.IsSubPath(assetsPath, resourceFullPath) {
return "", errors.New("exporting non-asset files from encrypted notebooks is not supported")
}
if encryptedBoxID == "" {
encryptedBoxID = boxID
} else if encryptedBoxID != boxID {
return "", errors.New("exporting resources across encrypted notebook boundaries is not supported")
}
}
if encryptedBoxID != "" && hasNormalResource {
return "", errors.New("exporting encrypted and normal notebook resources together is not supported")
}
return
}
func ExportPreview(id string, fillCSSVar bool, accessChecker ...EmbedBlockAccessChecker) (retStdHTML string) {
if exportErr := withExportReadLockByBlockID(id, func() error {
blockRefMode := Conf.Export.BlockRefMode
bt := getExportBlockTree(id)
if nil == bt {
return nil
}
tree := prepareExportTree(bt)
if numberErr := applyHeadingNumbersForExport(tree, bt, false); nil != numberErr {View on GitHub (pinned to 9f775e8a12)