siyuan-note/siyuan · error

accessing assets in encrypted notebook [%s] is not supported

Error message

accessing assets in encrypted notebook [%s] is not supported

What it means

Returned by ResolveDataAssetPath when the notebook identified by the first path segment is encrypted (IsEncryptedBox returns true). ResolveDataAssetPath deliberately refuses to resolve assets inside encrypted notebooks — encrypted assets are accessed through ReadAssetBytesInBox (which decrypts), not through this generic resolver. The notebook ID is interpolated.

Source

Thrown at kernel/model/assets.go:932

	assetDirIndex := -1
	switch {
	case len(parts) > 1 && parts[0] == "assets":
		assetDirIndex = 0
	case len(parts) > 2 && ast.IsNodeIDPattern(parts[0]):
		for i := 1; i < len(parts)-1; i++ {
			if parts[i] == "assets" {
				assetDirIndex = i
				break
			}
		}
		if assetDirIndex > 0 {
			boxConfPath := filepath.Join(util.DataDir, parts[0], ".siyuan", "conf.json")
			if !filelock.IsExist(boxConfPath) {
				err = fmt.Errorf("asset path does not belong to a notebook: %s", assetPath)
				return
			}
			if IsEncryptedBox(parts[0]) {
				err = fmt.Errorf("accessing assets in encrypted notebook [%s] is not supported", parts[0])
				return
			}
		}
	}
	if assetDirIndex < 0 {
		err = fmt.Errorf("path is not under an assets directory: %s", assetPath)
		return
	}

	assetRootParts := parts[:assetDirIndex+1]
	assetRoot := filepath.Join(util.DataDir, filepath.FromSlash(strings.Join(assetRootParts, "/")))
	if !gulu.File.IsSubPath(assetRoot, absPath) {
		err = fmt.Errorf("path is not a child of assets directory: %s", assetPath)
		return
	}

	resolvedRoot, evalErr := filepath.EvalSymlinks(assetRoot)
	if evalErr != nil {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. For encrypted-notebook assets, use ReadAssetBytesInBox(boxID, relativePath) instead, which acquires the read lock and decrypts.
  2. Ensure the notebook is unlocked in this session before reading.
  3. Route encrypted-asset access through APIs that are encryption-aware, not through ResolveDataAssetPath.

Example fix

// before — generic resolver rejects encrypted notebook
rel, abs, err := model.ResolveDataAssetPath("<encBoxID>/assets/x.png")

// after — use the encryption-aware read path
data, err := model.ReadAssetBytesInBox("<encBoxID>", "assets/x.png")
Defensive patterns

Strategy: validation

Validate before calling

parts := strings.Split(filepath.ToSlash(filepath.Clean(assetPath)), "/")
if len(parts) > 2 && ast.IsNodeIDPattern(parts[0]) && model.IsEncryptedBox(parts[0]) {
    // route to the encryption-aware API instead
    return model.ReadAssetBytesInBox(parts[0], assetPath)
}

Prevention

When it happens

Trigger: Calling ResolveDataAssetPath with a path whose notebook prefix is an encrypted notebook, e.g. "<encBoxID>/assets/x.png?box=<encBoxID>". This is by-design enforcement, not a transient error.

Common situations: Generic asset tooling (thumbnails, export, search-asset-content) tries to touch an encrypted notebook's assets without going through the encryption-aware read path.

Related errors


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