siyuan-note/siyuan · error
write file failed:
Error message
write file failed:
What it means
For binary (non-text) HTTP responses, HTTPRequest saves the already-read body bytes to <TempDir>/import/<filename> with os.WriteFile(filePath, respBody, 0644). If the file write fails, the error is wrapped as "write file failed: <cause>". The directory was created successfully, so failures here are usually about the specific file: permissions, disk space, or name collisions with unwritable entries.
Source
Thrown at kernel/util/httprequest.go:368
if resp.ContentLength > maxReadBytes {
return statusCode, contentType, "", errors.New("response too large")
}
respBody, rerr := io.ReadAll(io.LimitReader(resp.Body, maxReadBytes))
if rerr != nil {
return statusCode, contentType, "", errors.New("read body failed: " + rerr.Error())
}
// 二进制响应落盘,返回文件路径,供智能体按需进一步处理。
if !isTextContentType(contentType) {
importDir := filepath.Join(TempDir, "import")
if merr := os.MkdirAll(importDir, 0755); merr != nil {
return statusCode, contentType, "", errors.New("create import dir failed: " + merr.Error())
}
filename := extractFilename(rawURL, contentType)
filePath := filepath.Join(importDir, filename)
if werr := os.WriteFile(filePath, respBody, 0644); werr != nil {
return statusCode, contentType, "", errors.New("write file failed: " + werr.Error())
}
return statusCode, contentType, fmt.Sprintf("Saved to: %s (%d bytes)", filePath, len(respBody)), nil
}
return statusCode, contentType, truncateRunes(string(respBody), maxHTTPRequestChars), nil
}
// isTextContentType 判断 Content-Type 是否为可直接展示给智能体的文本类响应。
// 覆盖 text/*、application/json、application/xml、application/*+json 等。
func isTextContentType(contentType string) bool {
ct := strings.ToLower(strings.TrimSpace(strings.SplitN(contentType, ";", 2)[0]))
if ct == "" {
return false
}
if strings.HasPrefix(ct, "text/") {
return true
}
switch ct {View on GitHub (pinned to 8641553a1f)
Solutions
- Read the wrapped cause after "write file failed: " to distinguish ENOSPC (free disk space) from EACCES/EPERM (permissions)
- Free disk space on the volume holding the workspace temp directory if the cause is no-space
- Check ownership/permissions of the <TempDir>/import directory and remove conflicting entries with the same name
- Retry the request; if a specific server's filename is the problem, note the URL and inspect the extracted filename in <TempDir>/import
Example fix
// before $ df -h <workspace>/temp # check space $ ls -la <workspace>/temp/import // after: free space or fix perms $ chmod u+w <workspace>/temp/import $ rm -rf <workspace>/temp/import/<conflicting-dir>
Defensive patterns
Strategy: validation
Validate before calling
importDir := filepath.Join(util.TempDir, "import")
if stat, err := os.Stat(importDir); err != nil || !stat.IsDir() {
return errors.New("import dir missing or not a directory")
}
// check writability
probe := filepath.Join(importDir, ".write-probe")
if err := os.WriteFile(probe, nil, 0644); err != nil {
return fmt.Errorf("import dir not writable: %w", err)
}
os.Remove(probe) Try / catch
status, ct, text, err := util.HTTPRequest("GET", binaryURL, nil, "")
if err != nil && strings.Contains(err.Error(), "write file failed") {
if strings.Contains(err.Error(), "no space left") {
return errors.New("free disk space before downloading binary responses")
}
return fmt.Errorf("check permissions on %s: %w", filepath.Join(util.TempDir, "import"), err)
} Prevention
- Monitor free disk space on the workspace volume; binary responses can be up to 10 MB each
- Keep the <TempDir>/import directory writable by the SiYuan process user
- Clear stale/conflicting entries from the import directory periodically
When it happens
Trigger: HTTPRequest() with a binary Content-Type where os.WriteFile to <TempDir>/import/<extracted filename> fails — disk full, permission denied, the target path exists as a directory, or a filename extracted from URL/Content-Type contains path-hostile content.
Common situations: Full disk after a large download, another process holding the file with restrictive locks/ACLs, an 'import' directory whose files were made read-only, or servers sending Content-Disposition/URLs that extract to unusual filenames.
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
- read session file failed: %w
- create session dir failed: %w
- %w: read Vault config directory: %v
- create inline styles directory failed: %w
- write inline styles failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/3b8c56f258fdbd5b.
Report an issue: GitHub.