siyuan-note/siyuan · error

write file failed: %s

Error message

write file failed: %s

What it means

Raised by HTTPRequest after the import directory is created but os.WriteFile cannot persist the binary response body to TempDir/import/<filename>. The %s is the wrapped OS write error. The filename comes from extractFilename(rawURL, contentType), which derives it from the URL path or a random string plus a content-type extension.

Source

Thrown at kernel/util/httprequest.go:121

	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
}

// sendByMethod 按 method 分发请求,统一走 NewBrowserRequest 返回的 *req.Request。
func sendByMethod(request *req.Request, method, rawURL string) (*req.Response, error) {
	switch method {
	case "GET", "":
		return request.Get(rawURL)
	case "POST":
		return request.Post(rawURL)
	case "PUT":
		return request.Put(rawURL)
	case "DELETE":
		return request.Delete(rawURL)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Check free space on the TempDir volume (df) and free space or raise maxHTTPRequestFileBytes awareness before large downloads.
  2. Inspect the resolved filename in the error path for illegal characters and sanitize the URL or set a Content-Disposition-friendly name upstream.
  3. Ensure no other process (sync client, AV) holds an exclusive lock on TempDir/import.
  4. Retry the http_request; transient write failures (lock, momentary full disk) often clear.

Example fix

// before: large binary write fails on full disk
// after: preflight free space before calling HTTPRequest for big assets
var stat syscall.Statfs_t
if err := syscall.Statfs(util.TempDir, &stat); err == nil {
    free := stat.Bavail * uint64(stat.Bsize)
    if free < uint64(maxHTTPRequestFileBytes) {
        return errors.New("insufficient disk space for binary download")
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight free-space check for binary downloads.
var stat syscall.Statfs_t
if err := syscall.Statfs(filepath.Join(util.TempDir, "import"), &stat); err == nil {
    free := stat.Bavail * uint64(stat.Bsize)
    if free < 10*1024*1024 {
        return errors.New("insufficient disk space for binary download")
    }
}

Try / catch

statusCode, contentType, text, err := util.HTTPRequest(method, rawURL, headers, body)
if err != nil && strings.Contains(err.Error(), "write file failed") {
    // transient: disk full / lock; surface to caller, suggest retry after cleanup
    return fmt.Errorf("binary save failed (free space / lock?): %w", err)
}

Prevention

When it happens

Trigger: A non-text response is written with os.WriteFile(filePath, respBody, 0644) and the write fails. Causes: destination volume is full, the resolved filename contains characters the filesystem rejects, the path is too long, or the directory was removed between MkdirAll and WriteFile (TOCTOU).

Common situations: Disk full when the agent downloads a large binary (up to maxHTTPRequestFileBytes = 10 MiB); a URL whose final path segment contains illegal filename characters on the host OS; antivirus or sync software locking the import directory; concurrent http_request calls racing on the same filename.

Related errors


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