siyuan-note/siyuan · error

exporting non-asset files from encrypted notebooks is not…

Error message

exporting non-asset files from encrypted notebooks is not supported

What it means

In encrypted notebooks the exporter only supports asset files (files under <boxID>/assets/). If a resource path inside an encrypted box resolves outside the assets directory, exportResourcesEncryptedBox rejects it because non-asset files in encrypted boxes cannot be safely read/exported in plaintext form.

Solutions

  1. Only pass files located in the notebook's assets/ directory when the box is encrypted
  2. Export non-asset encrypted content via the dedicated document/AV export APIs instead of ExportResources
  3. Check IsEncryptedBox(boxID) and reject non-asset paths in the caller beforehand

Example fix

// before
ExportResources(["20240101120000-abcdefg/storage/av/20240101-xxx.av"]) // inside encrypted box
// after
ExportResources(["20240101120000-abcdefg/assets/image.png"])
Defensive patterns

Strategy: validation

Validate before calling

async function isExportableEncryptedResource(resourcePath) {
  const boxID = extractBoxIDFromAssetsPath(resourcePath);
  if (!boxID) return false;
  const assetsPrefix = boxID + '/assets/';
  return resourcePath.startsWith(assetsPrefix);
}

Type guard

const isAsset = p => p.split('/').slice(0,2).join('/') === boxID + '/assets';

Try / catch

try {
  await exportResources(paths);
} catch (e) {
  if (String(e).includes('non-asset files from encrypted notebooks')) {
    notifyUser('Only assets/ files can be exported from encrypted notebooks');
  }
}

Prevention

When it happens

Trigger: ExportResources called with a path inside an encrypted notebook that is not under <boxID>/assets/ (e.g. a .sy document, storage/av database file, or temp file inside the encrypted box).

Common situations: Plugin tries to export a database (storage/av) or custom file from an encrypted notebook; a stale/moved resource path now resolves under the box root instead of assets/.

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


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/99d8e2f12f2dc6db. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/export.go:1013

}

// 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, accessChecker ...EmbedBlockAccessChecker) (retStdHTML string) {
	if exportErr := withExportReadLockByBlockID(id, func() error {
		blockRefMode := Conf.Export.BlockRefMode
		bt := getExportBlockTree(id)
		if nil == bt {

View on GitHub (pinned to 9f775e8a12)