sipeed/picoclaw · error
failed to close tts audio file: %w
Error message
failed to close tts audio file: %w
What it means
file.Close() returned an error at pkg/audio/tts/tts.go:155 after io.Copy succeeded. On Go, deferred buffer flushing happens at close, so this usually means the final flush failed: ENOSPC (disk filled exactly at the end), NFS write-back error, or storage backend fault. The removeTemp defer then deletes the temp file, so the failed audio does not linger.
Source
Thrown at pkg/audio/tts/tts.go:155
return "", fmt.Errorf("failed to create temp file: %w", err)
}
removeTemp := true
defer func() {
if removeTemp {
_ = os.Remove(file.Name())
}
}()
_, err = io.Copy(file, stream)
if err != nil {
_ = file.Close()
return "", fmt.Errorf("failed to write tts audio: %w", err)
}
err = file.Close()
if err != nil {
return "", fmt.Errorf("failed to close tts audio file: %w", err)
}
filename = strings.TrimSpace(filename)
if filename == "" {
filename = fmt.Sprintf("tts-%d%s", time.Now().Unix(), fileExt)
}
ext := strings.ToLower(filepath.Ext(filename))
if ext == "" {
filename += fileExt
} else if ext != fileExt {
filename = strings.TrimSuffix(filename, filepath.Ext(filename)) + fileExt
}
scope := fmt.Sprintf("tool:send_tts:%s:%s:%d", channel, chatID, time.Now().UnixNano())
ref, err := store.Store(file.Name(), media.MediaMeta{
Filename: filename,
ContentType: contentType,View on GitHub (pinned to 49183d7e8d)
Solutions
- Check disk space/quota for the media temp dir filesystem: df -h <dir>
- If the temp dir is on NFS/network storage, move it to local disk — close-time EIO is a classic NFS symptom
- Free space and retry; the temp file is auto-removed so nothing to clean up
- If it recurs with plenty of space, capture the errno from the wrapped error and inspect storage health (dmesg for I/O errors)
Example fix
# before: media temp dir on NFS, close() flush fails with EIO # config: temp_dir = "/mnt/nfs/media" # after: spool locally, let the store handle remote persistence # config: temp_dir = "/var/tmp/picoclaw-media"
Defensive patterns
Strategy: try-catch
Validate before calling
func scratchDirHealthy() error {
var stat syscall.Statfs_t
if err := syscall.Statfs(media.TempDir(), &stat); err != nil {
return err
}
if stat.Bavail*uint64(stat.Bsize) < 50*1024*1024 {
return fmt.Errorf("less than 50MB free on temp dir volume")
}
return nil
} Try / catch
if _, err := tts.SynthesizeToStore(ctx, text, ch, chatID, name); err != nil {
if strings.Contains(err.Error(), "failed to close tts audio file") {
// final flush failed: almost always ENOSPC or storage fault
freeDiskSpace(); return err // temp file is auto-removed by the lib
}
return err
} Prevention
- Keep the media temp dir on local disk, not NFS
- Alert on <5% free space on the temp volume
- Treat close-after-copy errors as storage faults, not transient network noise
When it happens
Trigger: Disk becoming full between the copy finishing and close flushing buffered blocks; NFS/overlayfs returning EIO on final flush; the underlying file already invalidated by the OS (forced unmount, quota enforcement).
Common situations: Containers with size-capped writable layers (device-mapper quota) filling mid-request; network filesystems for the media temp dir; quota limits hit on shared storage.
Related errors
- failed to copy file content: %w
- failed to create media temp dir: %w
- failed to create temp file: %w
- failed to write tts audio: %w
- close temp file: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/6e5b406103857122.
Report an issue: GitHub.