{"record":{"id":"2faaa031b509bd0b","repo":"sipeed/picoclaw","slug":"failed-to-read-audio-file-w","errorCode":null,"errorMessage":"failed to read audio file: %w","messagePattern":"failed to read audio file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/asr/audio_model_transcriber.go","lineNumber":59,"sourceCode":"\t}\n\n\treturn &AudioModelTranscriber{\n\t\tprovider: provider,\n\t\tmodelID:  modelID,\n\t\tprompt:   defaultTranscriptionPrompt,\n\t}\n}\n\nfunc (t *AudioModelTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {\n\tlogger.InfoCF(\"voice\", \"Starting audio model transcription\", map[string]any{\n\t\t\"audio_file\": audioFilePath,\n\t\t\"model\":      t.modelID,\n\t})\n\n\taudioBytes, err := os.ReadFile(audioFilePath)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to read audio file\", map[string]any{\"path\": audioFilePath, \"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to read audio file: %w\", err)\n\t}\n\n\tformat, err := utils.AudioFormat(audioFilePath)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to detect audio format\", map[string]any{\"path\": audioFilePath, \"error\": err})\n\t\treturn nil, err\n\t}\n\n\tresp, err := t.provider.Chat(ctx, []providers.Message{\n\t\t{\n\t\t\tRole:    \"user\",\n\t\t\tContent: t.prompt,\n\t\t\tMedia: []string{\n\t\t\t\tfmt.Sprintf(\"data:audio/%s;base64,%s\", format, base64.StdEncoding.EncodeToString(audioBytes)),\n\t\t\t},\n\t\t},\n\t}, nil, t.modelID, map[string]any{\n\t\t\"temperature\": 0,","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/asr/audio_model_transcriber.go#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresp, err := transcriber.Transcribe(ctx, \"audio/input.mp3\")\nif err != nil { return err }\n\n// after\nif fi, err := os.Stat(\"audio/input.mp3\"); err != nil || fi.IsDir() {\n    return fmt.Errorf(\"audio file not ready: %w\", err)\n}\nresp, err := transcriber.Transcribe(ctx, \"audio/input.mp3\")\nif err != nil {\n    if errors.Is(err, fs.ErrNotExist) {\n        return fmt.Errorf(\"audio file disappeared before transcription: %w\", err)\n    }\n    return err\n}","handlingStrategy":"validation","validationCode":"// Run before Transcribe:\nfunc audioReadable(path string) error {\n    fi, err := os.Stat(path)\n    if err != nil { return fmt.Errorf(\"audio file inaccessible: %w\", err) }\n    if fi.IsDir() { return fmt.Errorf(\"audio path is a directory: %s\", path) }\n    if fi.Size() == 0 { return fmt.Errorf(\"audio file is empty: %s\", path) }\n    if fi.Mode().Perm()&0o400 == 0 { return fmt.Errorf(\"audio file not readable: %s\", path) }\n    return nil\n}","typeGuard":"func isFileMissing(err error) bool {\n    return errors.Is(err, fs.ErrNotExist)\n}\nfunc isPermissionErr(err error) bool {\n    return errors.Is(err, fs.ErrPermission)\n}","tryCatchPattern":"if err := transcriber.Transcribe(ctx, path); err != nil {\n    switch {\n    case errors.Is(err, fs.ErrNotExist):\n        // producer bug: file gone before transcription\n    case errors.Is(err, fs.ErrPermission):\n        // fix ownership/permissions, do not retry\n    default:\n        // real read error (I/O): log and surface\n    }\n}","preventionTips":["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."],"tags":["filesystem","audio","transcription","go","picoclaw"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}