{"record":{"id":"80934dc82eb37455","repo":"sipeed/picoclaw","slug":"failed-to-get-file-info-w","errorCode":null,"errorMessage":"failed to get file info: %w","messagePattern":"failed to get file info: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/asr/elevenlabs_transcriber.go","lineNumber":60,"sourceCode":"\t\t\tTimeout: 120 * time.Second,\n\t\t},\n\t}\n}\n\nfunc (t *ElevenLabsTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {\n\tlogger.InfoCF(\"voice\", \"Starting ElevenLabs transcription\", map[string]any{\"audio_file\": audioFilePath})\n\n\taudioFile, err := os.Open(audioFilePath)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to open audio file\", map[string]any{\"path\": audioFilePath, \"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to open audio file: %w\", err)\n\t}\n\tdefer audioFile.Close()\n\n\tfileInfo, err := audioFile.Stat()\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to get file info\", map[string]any{\"path\": audioFilePath, \"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to get file info: %w\", err)\n\t}\n\n\tlogger.DebugCF(\"voice\", \"Audio file details\", map[string]any{\n\t\t\"size_bytes\": fileInfo.Size(),\n\t\t\"file_name\":  filepath.Base(audioFilePath),\n\t})\n\n\tvar requestBody bytes.Buffer\n\twriter := multipart.NewWriter(&requestBody)\n\n\tpart, err := writer.CreateFormFile(\"file\", filepath.Base(audioFilePath))\n\tif err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to create form file\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to create form file: %w\", err)\n\t}\n\n\tif _, err = io.Copy(part, audioFile); err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to copy file content\", map[string]any{\"error\": err})","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/asr/elevenlabs_transcriber.go#L42-L78","documentation":"Thrown when audioFile.Stat() fails immediately after a successful os.Open in ElevenLabsTranscriber.Transcribe. On POSIX this is rare: the fd is already open, so Stat only fails if the fd is invalid (closed concurrently) or the kernel cannot fstat (NFS/virtual filesystems under load). The error wraps a *os.PathError with Op 'stat'.","triggerScenarios":"The *os.File handle is closed by another goroutine between Open and Stat; the file lives on a failing network mount (NFS stale handle, EIO); filesystem returned an unexpected errno during fstat.","commonSituations":"Data race where a cleanup goroutine closes/deletes the temp file while transcription starts; flaky NFS/FUSE mount in containers; extremely rare on local ext4.","solutions":["Audit for concurrent Close/deletion of the same audio file handle (go test -race).","Copy the audio into a local temp file before transcribing when the source is a network mount.","Retry once; if Stat keeps failing the storage layer is unhealthy."],"exampleFix":"// before\naudioFile, _ := os.Open(p)\nfi, err := audioFile.Stat()\n\n// after: single ownership, no concurrent close\naudioFile, err := os.Open(p)\nif err != nil { return nil, fmt.Errorf(\"open: %w\", err) }\ndefer audioFile.Close()\nfi, err := audioFile.Stat()\nif err != nil { return nil, fmt.Errorf(\"stat after open (fs unhealthy?): %w\", err) }","handlingStrategy":"validation","validationCode":"// Pre-check the same stat the transcriber performs, so failures surface on your terms:\nfi, err := os.Stat(path)\nif err != nil { return fmt.Errorf(\"stat failed before transcription: %w\", err) }\nif fi.Size() == 0 { return errors.New(\"empty audio file\") }","typeGuard":"func isPathStatErr(err error) bool {\n    var pe *os.PathError\n    return errors.As(err, &pe) && pe.Op == \"stat\"\n}","tryCatchPattern":"if _, err := el.Transcribe(ctx, path); err != nil {\n    if isPathStatErr(err) {\n        // storage layer or a race; copy file locally and retry once\n        local, cerr := copyToLocal(path)\n        if cerr == nil { _, err = el.Transcribe(ctx, local) }\n    }\n    if err != nil { return err }\n}","preventionTips":["Never share the audio file handle across goroutines.","Copy network-mounted audio to local disk before transcription.","Enable the race detector in CI for the audio pipeline."],"tags":["filesystem","concurrency","elevenlabs","audio","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}