siyuan-note/siyuan · error
remove unused asset [%s] failed: %w
Error message
remove unused asset [%s] failed: %w
What it means
Returned by RemoveUnusedAsset (assets.go:1473) when filelock.RemoveWithoutFatal fails to delete the asset file after it was successfully copied to history. The asset has already been removed from the SQL index and cache (sql.BatchRemoveAssetsQueue, cache.RemoveAssetHash) and the remove event has been dispatched on mobile, so the file is the last on-disk artifact. The %w preserves the underlying remove error.
Source
Thrown at kernel/model/assets.go:1473
historyPath := filepath.Join(historyDir, filepath.FromSlash(relativePath))
if err = filelock.Copy(absPath, historyPath); err != nil {
err = fmt.Errorf("copy unused asset [%s] to history failed: %w", absPath, err)
return
}
hash, _ := util.GetEtag(absPath)
sql.BatchRemoveAssetsQueue([]string{hash})
cache.RemoveAssetHash(hash)
if util.IsMobileContainer() {
HandleAssetsRemoveEvent(absPath)
}
if err = filelock.RemoveWithoutFatal(absPath); err != nil {
logging.LogErrorf("remove unused asset [%s] failed: %s", absPath, err)
util.PushErrMsg(fmt.Sprintf("%s", err), 7000)
err = fmt.Errorf("remove unused asset [%s] failed: %w", absPath, err)
return
}
ret = absPath
util.RemoveAssetText(relativePath)
IncSync()
indexHistoryDir(filepath.Base(historyDir), util.NewLute())
cache.RemoveAsset(relativePath)
return
}
func RenameAsset(oldPath, newName string) (newPath string, err error) {
util.PushEndlessProgress(Conf.Language(110))
defer util.PushClearProgress()
oldCleanPath := AssetPathWithoutQuery(oldPath)View on GitHub (pinned to 251596fc0d)
Solutions
- Inspect the wrapped err for EBUSY/EACCES/EPERM and close whatever process holds the file, then retry RemoveUnusedAsset.
- Verify the assets directory is writable and not on a read-only mount.
- Note the asset was already de-indexed and copied to history; if you retry and the source is now gone the function will still attempt removal and may report ENOENT — treat a vanished file as success rather than re-erroring.
Example fix
// before: any remove error aborts after index/cache already updated
if err = filelock.RemoveWithoutFatal(absPath); err != nil {
err = fmt.Errorf("remove unused asset [%s] failed: %w", absPath, err)
return
}
// after: a vanished file is not fatal once history copy succeeded
if err = filelock.RemoveWithoutFatal(absPath); err != nil {
if os.IsNotExist(err) {
err = nil
} else {
err = fmt.Errorf("remove unused asset [%s] failed: %w", absPath, err)
return
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Close any external handle before deletion; the kernel cannot force-remove a locked file. if (window.siyuan.mobile) await closeAssetViewers(absPath)
Try / catch
// The asset is already de-indexed and history-copied; a vanished file is effectively success.
try {
await fetchPost('/api/asset/removeUnusedAsset', { path: p })
} catch (e) {
if (/no such file|not exist/i.test(String(e.message))) return
throw e
} Prevention
- Make sure no process holds the asset open (PDF reader, image editor, indexer).
- Keep the assets directory writable and not on a read-only mount.
- After a failure, re-run RemoveUnusedAsset — the prior history copy is preserved.
When it happens
Trigger: Calling /api/asset/removeUnusedAsset when the asset file cannot be deleted: permission denied, file held open by another process, read-only mount, or the path is a directory unexpectedly. The preceding history copy succeeded, so the data is preserved but deletion is blocked.
Common situations: File open in an external viewer/editor (PDF reader, image editor); read-only assets directory; Windows file lock by antivirus/indexer; the asset was converted to a directory by a sync conflict.
Related errors
- copy unused asset [%s] to history failed: %w
- marketplace package install path already exists
- install local marketplace package failed: %w; rollback faile
- remove community package [%s] failed
- --output is required for docx
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/9f2f16c3c706b792.
Report an issue: GitHub.