sipeed/picoclaw · error
failed to read audio file: %w
Error message
failed to read audio file: %w
What it means
Thrown by AudioModelTranscriber.Transcribe when os.ReadFile cannot read the audio file before base64-encoding it into a chat message. The returned error wraps a *os.PathError naming the failing syscall (open/read/stat) and reason. The transcription never reaches the model provider; the wrapped message 'failed to read audio file' is added on top of the OS error.
Source
Thrown at pkg/audio/asr/audio_model_transcriber.go:59
}
return &AudioModelTranscriber{
provider: provider,
modelID: modelID,
prompt: defaultTranscriptionPrompt,
}
}
func (t *AudioModelTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {
logger.InfoCF("voice", "Starting audio model transcription", map[string]any{
"audio_file": audioFilePath,
"model": t.modelID,
})
audioBytes, err := os.ReadFile(audioFilePath)
if err != nil {
logger.ErrorCF("voice", "Failed to read audio file", map[string]any{"path": audioFilePath, "error": err})
return nil, fmt.Errorf("failed to read audio file: %w", err)
}
format, err := utils.AudioFormat(audioFilePath)
if err != nil {
logger.ErrorCF("voice", "Failed to detect audio format", map[string]any{"path": audioFilePath, "error": err})
return nil, err
}
resp, err := t.provider.Chat(ctx, []providers.Message{
{
Role: "user",
Content: t.prompt,
Media: []string{
fmt.Sprintf("data:audio/%s;base64,%s", format, base64.StdEncoding.EncodeToString(audioBytes)),
},
},
}, nil, t.modelID, map[string]any{
"temperature": 0,View on GitHub (pinned to 49183d7e8d)
Solutions
- Check that audioFilePath exists and is a regular, readable file before calling Transcribe (os.Stat + mode check).
- Print the wrapped OS error (errors.Unwrap / err.Error()) to see ENOENT vs EACCES vs EISDIR and fix the path or permissions accordingly.
- If the file is produced by a recorder, make it write to a stable absolute path under a directory the process owns.
- If the path comes from user input, resolve and validate it against an allow-listed directory.
Example fix
// before
resp, err := transcriber.Transcribe(ctx, "audio/input.mp3")
if err != nil { return err }
// after
if fi, err := os.Stat("audio/input.mp3"); err != nil || fi.IsDir() {
return fmt.Errorf("audio file not ready: %w", err)
}
resp, err := transcriber.Transcribe(ctx, "audio/input.mp3")
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("audio file disappeared before transcription: %w", err)
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
// Run before Transcribe:
func audioReadable(path string) error {
fi, err := os.Stat(path)
if err != nil { return fmt.Errorf("audio file inaccessible: %w", err) }
if fi.IsDir() { return fmt.Errorf("audio path is a directory: %s", path) }
if fi.Size() == 0 { return fmt.Errorf("audio file is empty: %s", path) }
if fi.Mode().Perm()&0o400 == 0 { return fmt.Errorf("audio file not readable: %s", path) }
return nil
} Type guard
func isFileMissing(err error) bool {
return errors.Is(err, fs.ErrNotExist)
}
func isPermissionErr(err error) bool {
return errors.Is(err, fs.ErrPermission)
} Try / catch
if err := transcriber.Transcribe(ctx, path); err != nil {
switch {
case errors.Is(err, fs.ErrNotExist):
// producer bug: file gone before transcription
case errors.Is(err, fs.ErrPermission):
// fix ownership/permissions, do not retry
default:
// real read error (I/O): log and surface
}
} Prevention
- Write recordings to a temp name and rename atomically so Transcribe never sees a partial file.
- Use absolute paths owned by the service user.
- Pre-validate with os.Stat before every Transcribe call.
When it happens
Trigger: os.ReadFile(audioFilePath) fails because: the path does not exist (ENOENT), permission denied (EACCES, common when the agent runs as another user), the path is a directory (EISDIR), or the file was deleted between recording and transcription. Occurs before provider.Chat is called.
Common situations: Agent writes audio to a temp dir that another process cleaned up; relative path evaluated from a different working directory; container volume mount hides the file; file owned by root while the service runs unprivileged.
Related errors
- failed to open audio file: %w
- failed to open audio file %s: %w
- seahorse: create engine: %w
- transcription request failed: %w
- failed to get file info: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/2faaa031b509bd0b.
Report an issue: GitHub.