sipeed/picoclaw · error
close temp file: %w
Error message
close temp file: %w
What it means
Closing the spooled inbound media file failed (media.go:341-343). On local filesystems Close is where buffered data is flushed, so a full disk (ENOSPC) or I/O error (EIO) first appears here even though Write succeeded; NFS/overlayfs can also defer write-back errors to Close. The code unlinks the file on this failure, so nothing partial is stored.
Source
Thrown at pkg/channels/wecom/media.go:343
ext = inferMediaExt(contentType, fallbackExt)
}
mediaDir := filepath.Join(os.TempDir(), "picoclaw_media")
if mkdirErr := os.MkdirAll(mediaDir, 0o700); mkdirErr != nil {
return "", fmt.Errorf("mkdir media dir: %w", mkdirErr)
}
tmpFile, err := os.CreateTemp(mediaDir, msgID+"-*"+ext)
if err != nil {
return "", fmt.Errorf("create temp file: %w", err)
}
tmpPath := tmpFile.Name()
if _, writeErr := tmpFile.Write(data); writeErr != nil {
_ = tmpFile.Close()
_ = os.Remove(tmpPath)
return "", fmt.Errorf("write temp file: %w", writeErr)
}
if closeErr := tmpFile.Close(); closeErr != nil {
_ = os.Remove(tmpPath)
return "", fmt.Errorf("close temp file: %w", closeErr)
}
ref, err := store.Store(tmpPath, media.MediaMeta{
Filename: filename,
ContentType: contentType,
Source: "wecom",
CleanupPolicy: media.CleanupPolicyDeleteOnCleanup,
}, scope)
if err != nil {
_ = os.Remove(tmpPath)
return "", err
}
return ref, nil
}
func detectLocalWeComContentType(localPath, hint string) string {
contentType := normalizeWeComContentType(hint)
if !isGenericWeComContentType(contentType) {View on GitHub (pinned to 49183d7e8d)
Solutions
- Treat it exactly like a write failure: check dmesg/df and the wrapped *fs.PathError errno
- Rule out deferred-ENOSPC by checking free space at failure time; free space and retry the message
- Do not put the media spool on NFS - use local SSD or a volume with reliable write-back
- If EIO persists: inspect device health (smartctl) before re-accepting media traffic
Defensive patterns
Strategy: try-catch
Type guard
import "io/fs"
func isFsErr(err error, target error) bool { return errors.Is(err, target) }
// usage: isFsErr(err, fs.ErrNotExist), isFsErr(err, fs.ErrPermission) Try / catch
// Close errors are real write failures - never _ = f.Close() on data files
err = tmpFile.Write(data)
if cerr := tmpFile.Close(); err == nil && cerr != nil {
_ = os.Remove(tmpPath)
err = fmt.Errorf("close temp file: %w", cerr)
} Prevention
- check the errno in the wrapped fs.PathError before assuming corruption
- keep the spool off NFS and overlay-on-thin-provisioning
- test the deployment by spooling a full-size 20 MiB file at startup
When it happens
Trigger: Filesystem accepted writes into cache but failed the flush at Close: ENOSPC after quota enforcement, EIO on failing disks, NFS stale-handle/async errors on a /tmp mounted over NFS.
Common situations: Overlayfs container layers with thin-provisioned storage that filled up; NFS home dirs used as TMPDIR; degraded RAID surfacing EIO.
Related errors
- close destination file %s: %w
- failed to close tts audio file: %w
- write temp file: %w
- write result: %w
- failed to copy file content: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/bde7b747abbd4a20.
Report an issue: GitHub.