siyuan-note/siyuan · error
failed to write file
Error message
failed to write file: %w
What it means
filelock.WriteFile failed after the parent directories were successfully created while servicing siyuan.storage.put. The wrapped error (%w) is the underlying cause — typically disk full, permission denied on the target file, or the target path existing as a directory. The write is performed under SiYuan's file lock to avoid concurrent-write corruption.
Solutions
- Inspect the wrapped OS error in the rejection for the concrete cause
- Free disk space if the error indicates ENOSPC, and confirm workspace writability
- Ensure the target path is not an existing directory; remove it or choose a different filename
- Retry the put — transient locks from sync/AV tools often clear within seconds
Example fix
// before
await siyuan.storage.put("cache", text); // "cache" is an existing directory
// after
await siyuan.storage.put("cache/page.html", text); // write into a file path instead Defensive patterns
Strategy: retry
Validate before calling
if (!path || path.endsWith("/")) throw new TypeError("target must be a file path, not a directory"); Type guard
const isFilePath = (p) => typeof p === "string" && p.length > 0 && !p.endsWith("/"); Try / catch
try { await siyuan.storage.put(path, content); } catch (e) { if (String(e.message).includes("failed to write file")) { await delay(500); return siyuan.storage.put(path, content); } throw e; } Prevention
- Retry transient write failures with a short backoff (sync clients and AV locks clear quickly)
- Ensure the target path is not already a directory
- Monitor free disk space for plugins storing large blobs
- Prefer stable paths not touched by cloud-sync placeholders
When it happens
Trigger: The workspace disk is full or quota-exhausted; the target file is read-only or owned by another user; the target path resolves to an existing directory; antivirus/backup tools hold the file during the locked write on Windows.
Common situations: Plugins writing large blobs that fill the workspace volume; files made read-only by sync clients (OneDrive/iCloud placeholders); a previously created directory occupying the exact target path.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- failed to make directory
- failed to remove storage path from watcher
- panic during siyuan.storage.get
- panic during siyuan.storage.put
- path and content required
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0d8deb3fc9dbfb2f.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/api_storage.go:282
if lo.IsNil(err) {
if resolveErr := resolve(result); resolveErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.put resolve: %v", p.Name, resolveErr)
}
} else {
if rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.put reject: %v", p.Name, rejectErr)
}
}
return
}, nil)
}()
if mkdirErr := os.MkdirAll(filepath.Dir(abs), 0755); mkdirErr != nil {
err = fmt.Errorf("failed to make directory: %w", mkdirErr)
return
}
if writeErr := filelock.WriteFile(abs, []byte(content)); writeErr != nil {
err = fmt.Errorf("failed to write file: %w", writeErr)
return
}
return
}()
return
}, func(rt *goja.Runtime, result any, err error) {
if !lo.IsNil(err) {
if rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.put reject: %v", p.Name, rejectErr)
}
}
})
if runErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.put worker run: %v", p.Name, runErr)
if rejectErr := reject(rt.NewGoError(runErr)); rejectErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.put reject: %v", p.Name, rejectErr)
}View on GitHub (pinned to 9f775e8a12)