siyuan-note/siyuan · error
truncate file [%s] failed: %s
Error message
truncate file [%s] failed: %s
What it means
WriteFileByMmap writes a file by truncating it to the incoming data length, then memory-mapping and copying. This error fires when the f.Truncate call fails, aborting the write before any data is copied, and is raised for attribute-view saves and tree writes.
Source
Thrown at kernel/util/mmap.go:47
// WriteFileByMmap 使用内存映射将 data 原地覆写到 filePath。
//
// 流程:OpenFile(O_RDWR|O_CREATE) → Truncate 到精确长度 → mmap.Map(RDWR) →
// copy 写入 → Flush → Unmap,全程持有 filelock 的进程内互斥锁,避免并发写冲突。
//
// 相比 filelock.WriteFile(临时文件 + rename + fsync),此路径在进程级 I/O
// 计数(IO Write Bytes)上几乎不计——copy 是纯内存写,不经过 I/O 子系统,
// 只有 Flush 会产生极少量计入。出错时由调用方回退到 filelock.WriteFile。
func WriteFileByMmap(filePath string, data []byte) (err error) {
f, err := filelock.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return
}
defer filelock.CloseFile(f)
if err = f.Truncate(int64(len(data))); err != nil {
msg := fmt.Sprintf("truncate file [%s] failed: %s", filePath, err)
logging.LogError(msg)
err = errors.New(msg)
return
}
m, err := mmap.Map(f, mmap.RDWR, 0)
if err != nil {
msg := fmt.Sprintf("map file [%s] failed: %s", filePath, err)
logging.LogError(msg)
err = errors.New(msg)
return
}
defer m.Unmap()
copy(m, data)
if err = m.Flush(); err != nil {
msg := fmt.Sprintf("flush data [%s] failed: %s", filePath, err)
logging.LogError(msg)
err = errors.New(msg)
returnView on GitHub (pinned to 8641553a1f)
Solutions
- Read the embedded OS error (%s) to identify the cause (sharing violation, read-only fs, disk full)
- Close other processes locking the file (sync clients, editors, antivirus scans) and retry the save
- Check disk free space and that the workspace volume is writable
- Retry the operation — SiYuan will fall back to normal write paths in some callers
Example fix
// before err = WriteFileByMmap(absPath, data) // fails: file locked // after filelock.Release(absPath) // or close the external lock holder err = WriteFileByMmap(absPath, data)
Defensive patterns
Strategy: retry
Validate before calling
// before writing, ensure path is writable and not locked
if _, err := os.Stat(filePath); err != nil { return err }
if f, err := os.OpenFile(filePath, os.O_RDWR, 0644); err != nil { return err } else { f.Close() } Try / catch
if err := util.WriteFileByMmap(path, data); err != nil {
log.Printf("mmap write failed: %v", err)
time.Sleep(100*time.Millisecond)
err = util.WriteFileByMmap(path, data) // retry once after transient lock
} Prevention
- Close external file lock holders (sync clients, editors) before bulk saves
- Keep the workspace off read-only or network filesystems
- Monitor free disk space
- On Windows, exclude the workspace from real-time antivirus scans
When it happens
Trigger: Calling WriteFileByMmap (via SaveAttributeView or WriteTree) on a path where the OS refuses truncate: file locked by another process, read-only filesystem, disk full semantics on some platforms, or an invalid/disappearing file handle.
Common situations: Sync or indexing processes holding the .sy/.av file open on Windows; workspace on a read-only or full disk; file removed between open and truncate; antivirus locking data files.
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
- map file [%s] failed: %s
- flush data [%s] failed: %s
- install local marketplace package failed: %w; rollback faile
- read session file failed: %w
- create session dir failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/b17997e11a480548.
Report an issue: GitHub.