sipeed/picoclaw · error
failed to copy file content: %w
Error message
failed to copy file content: %w
What it means
Thrown when io.Copy(part, audioFile) fails while streaming the opened audio file into the multipart buffer in ElevenLabsTranscriber.Transcribe. This is a read error on the file descriptor mid-copy (the multipart side is an in-memory buffer that does not fail under normal conditions). The wrapped error is usually a *os.PathError with Op 'read'.
Source
Thrown at pkg/audio/asr/elevenlabs_transcriber.go:79
}
logger.DebugCF("voice", "Audio file details", map[string]any{
"size_bytes": fileInfo.Size(),
"file_name": filepath.Base(audioFilePath),
})
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
part, err := writer.CreateFormFile("file", filepath.Base(audioFilePath))
if err != nil {
logger.ErrorCF("voice", "Failed to create form file", map[string]any{"error": err})
return nil, fmt.Errorf("failed to create form file: %w", err)
}
if _, err = io.Copy(part, audioFile); err != nil {
logger.ErrorCF("voice", "Failed to copy file content", map[string]any{"error": err})
return nil, fmt.Errorf("failed to copy file content: %w", err)
}
if err = writer.WriteField("model_id", t.modelID); err != nil {
return nil, fmt.Errorf("failed to write model_id field: %w", err)
}
if err = writer.Close(); err != nil {
logger.ErrorCF("voice", "Failed to close multipart writer", map[string]any{"error": err})
return nil, fmt.Errorf("failed to close multipart writer: %w", err)
}
url := t.apiBase + "/v1/speech-to-text"
req, err := http.NewRequestWithContext(ctx, "POST", url, &requestBody)
if err != nil {
logger.ErrorCF("voice", "Failed to create request", map[string]any{"error": err})
return nil, fmt.Errorf("failed to create request: %w", err)
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Make sure the recording is fully written and closed before Transcribe is called (write to temp name, rename atomically).
- If reading from a mount, copy the file locally first (os.ReadFile then transcribe from a temp file).
- Check dmesg/system logs for disk errors if EIO recurs.
Example fix
// before: transcribe while recorder may still write
resp, err := t.Transcribe(ctx, livePath)
// after: atomic handoff via rename
tmp := livePath + ".part"
// recorder writes and closes tmp, then:
if err := os.Rename(tmp, livePath); err != nil { return err }
resp, err := t.Transcribe(ctx, livePath) Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the file is fully written and stable before transcription:
fi, err := os.Stat(path)
if err != nil { return err }
if fi.Size() == 0 { return errors.New("audio still being written") } Type guard
func isReadErr(err error) bool {
var pe *os.PathError
return errors.As(err, &pe) && pe.Op == "read"
} Try / catch
resp, err := el.Transcribe(ctx, path)
if err != nil {
if isReadErr(err) {
// mid-copy I/O failure: verify the file then retry once
if _, serr := os.Stat(path); serr == nil {
resp, err = el.Transcribe(ctx, path)
}
}
if err != nil { return err }
} Prevention
- Recorders: write to file.part, fsync, then os.Rename to the final name.
- Avoid transcribing FIFOs/devices; only regular files.
- Check disk health when EIO appears in logs.
When it happens
Trigger: The file is truncated or deleted-and-recreated while being read; EIO from a failing disk or stale NFS handle; the file is a named pipe or device whose writer closes early.
Common situations: Recorder still writing the file when transcription starts (partial file, then rewrite); audio captured on a network mount that drops mid-copy; path actually points at a FIFO.
Related errors
- failed to open audio file: %w
- failed to get file info: %w
- failed to read audio file: %w
- failed to create form file: %w
- failed to write model_id field: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/903da186031ac7da.
Report an issue: GitHub.