siyuan-note/siyuan · error

exporting non-asset files from encrypted notebooks is not su

Error message

exporting non-asset files from encrypted notebooks is not supported

What it means

Returned by exportResourcesEncryptedBox when a resource path resolves into an encrypted notebook (ExtractBoxIDFromAssetsPath returns a boxID that IsEncryptedBox confirms) but the path is NOT under that notebook's assets/ directory (gulu.File.IsSubPath fails). SiYuan only supports exporting asset files from encrypted notebooks via the managed assets folder; exporting arbitrary .sy or other files from an encrypted box is refused to avoid leaking ciphertext structure.

Source

Thrown at kernel/model/export.go:937

}

// exportResourcesEncryptedBox 校验资源导出是否跨越加密边界,并返回唯一允许的加密来源 boxID。
func exportResourcesEncryptedBox(resourcePaths []string) (encryptedBoxID string, err error) {
	hasNormalResource := false
	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) (retStdHTML string) {
	if exportErr := withExportReadLockByBlockID(id, func() error {
		blockRefMode := Conf.Export.BlockRefMode
		bt := getExportBlockTree(id)
		if nil == bt {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Only pass asset paths (under data/<box>/assets/) of encrypted notebooks to ExportResources.
  2. To export a document from an encrypted notebook, use ExportSYs/ExportMarkdownHTML/ExportDocx (the doc-level export path with the read-lock guard) instead.
  3. Filter resource paths at the caller to the encrypted box's assets/ subtree before calling.

Example fix

// before — passing a .sy path from an encrypted box
model.ExportResources([]string{"data/20230101abc/20230101xyz.sy"}, name)
// after — export the document via the doc-level path, or pass only assets
model.ExportResources([]string{"data/20230101abc/assets/image.png"}, name)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure encrypted-notebook resources are strictly under that box's assets/ dir
for _, p := range resourcePaths {
    full := filepath.Join(util.WorkspaceDir, p)
    boxID := ExtractBoxIDFromAssetsPath(full)
    if boxID != "" && IsEncryptedBox(boxID) {
        assetsPath := filepath.Join(util.DataDir, boxID, "assets")
        if !gulu.File.IsSubPath(assetsPath, full) {
            return fmt.Errorf("non-asset path in encrypted box %s: %s", boxID, p)
        }
    }
}

Type guard

// isEncryptedBoxAsset reports whether p is under the encrypted box's assets/ folder.
func isEncryptedBoxAsset(p string) bool {
    boxID := ExtractBoxIDFromAssetsPath(p)
    if boxID == "" || !IsEncryptedBox(boxID) {
        return false
    }
    return gulu.File.IsSubPath(filepath.Join(util.DataDir, boxID, "assets"), p)
}

Prevention

When it happens

Trigger: POST /api/export/exportResources with a path like data/<encrypted-box>/2023...sy or a file under the notebook root rather than under data/<encrypted-box>/assets/. The user (or a plugin) selected a document or internal file from an encrypted notebook instead of an asset.

Common situations: User tries to export a .sy doc or a non-asset file from an encrypted notebook via the resource-export endpoint instead of the document-export endpoint. Plugin iterates notebook files generically without filtering to assets/.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/99d8e2f12f2dc6db. Report an issue: GitHub.