siyuan-note/siyuan · warning

asset is not unused: %s

Error message

asset is not unused: %s

What it means

Thrown by ResolveUnusedDataAssetPath (kernel/model/assets.go:989) after the path resolved successfully but the asset is NOT present in the computed unused-asset list (UnusedAssets(false)). It is a business-rule guard, not a security or filesystem error: the caller asked to operate on an unused asset, but the asset is still referenced by some block, so the operation is refused.

Source

Thrown at kernel/model/assets.go:989

		err = fmt.Errorf("asset path resolves outside assets directory: %s", assetPath)
		return
	}

	relativePath = filepath.ToSlash(dataRelativePath)
	return
}

// ResolveUnusedDataAssetPath 解析 data 相对资源路径,并确认目标当前未被引用。
func ResolveUnusedDataAssetPath(assetPath string) (relativePath, absPath string, err error) {
	relativePath, absPath, err = ResolveDataAssetPath(assetPath)
	if err != nil {
		return
	}

	if unusedAssetsContainPath(relativePath, absPath, UnusedAssets(false)) {
		return
	}
	err = fmt.Errorf("asset is not unused: %s", relativePath)
	return
}

func unusedAssetsContainPath(relativePath, absPath string, items []*UnusedItem) bool {
	resolvedPath, _ := filepath.EvalSymlinks(absPath)
	for _, item := range items {
		if item.AbsPath != "" {
			resolvedItemPath, evalErr := filepath.EvalSymlinks(item.AbsPath)
			samePath, relErr := filepath.Rel(resolvedPath, resolvedItemPath)
			if resolvedPath != "" && evalErr == nil && relErr == nil && samePath == "." {
				return true
			}
			continue
		}
		itemPath := filepath.ToSlash(filepath.Clean(filepath.FromSlash(item.Item)))
		if itemPath == relativePath {
			return true
		}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Confirm the asset is genuinely unused: search the notebook for the filename and remove/replace every reference.
  2. Force the unused-asset index to refresh (re-index the relevant notebooks) so references are accurately counted, then retry.
  3. If you must remove a still-referenced asset, use the explicit delete API (which will also rewrite references) rather than the unused-asset path.
  4. Do not catch-and-ignore; removing a referenced asset would create broken links.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the asset is in the unused set before attempting an unused-asset operation.
unused := model.UnusedAssets(false)
found := false
for _, it := range unused {
    if it.Item == p || it.AbsPath == abs { found = true; break }
}
if !found { return fmt.Errorf("asset %s is still referenced; use the referenced-asset delete path", p) }

Try / catch

if _, _, err := model.ResolveUnusedDataAssetPath(p); err != nil {
    if strings.Contains(err.Error(), "not unused") {
        // still referenced — re-index, then retry, or use the explicit delete API that rewrites references
    }
}

Prevention

When it happens

Trigger: Calling ResolveUnusedDataAssetPath (used by cli/cmd/asset.go:120 for `siyuan asset` cleanup and by model.RemoveUnusedAsset / assets.go:1446) with an asset path whose file is still referenced in at least one document. The unused-asset scan re-checks references, and because the asset is referenced, it is absent from the unused list.

Common situations: Running a cleanup/delete on an asset the user believes is unused but is still embedded in a note; a reference that was thought to be deleted but remains in the blocktree (stale SQL rows not yet flushed); calling cleanup right after re-linking the asset elsewhere.

Related errors


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