{"record":{"id":"ab6112f8086be4bc","repo":"sipeed/picoclaw","slug":"failed-to-close-multipart-writer-w-ab6112","errorCode":null,"errorMessage":"failed to close multipart writer: %w","messagePattern":"failed to close multipart writer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/asr/whisper_transcriber.go","lineNumber":127,"sourceCode":"\n\tif _, copyErr := io.Copy(part, bytes.NewReader(data)); copyErr != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to copy whisper file content\", map[string]any{\"error\": copyErr})\n\t\treturn nil, fmt.Errorf(\"failed to copy file content: %w\", copyErr)\n\t}\n\n\tif err = writer.WriteField(\"model\", t.modelID); err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to write whisper model field\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to write model field: %w\", err)\n\t}\n\n\tif err = writer.WriteField(\"response_format\", \"json\"); err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to write whisper response_format field\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to write response_format field: %w\", err)\n\t}\n\n\tif err = writer.Close(); err != nil {\n\t\tlogger.ErrorCF(\"voice\", \"Failed to close whisper multipart writer\", map[string]any{\"error\": err})\n\t\treturn nil, fmt.Errorf(\"failed to close multipart writer: %w\", err)\n\t}\n\n\treturn t.doRequest(ctx, &requestBody, writer.FormDataContentType(), int64(len(data)))\n}\n\nfunc (t *WhisperTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {\n\tlogger.InfoCF(\"voice\", \"Starting whisper transcription\", map[string]any{\n\t\t\"audio_file\": audioFilePath,\n\t\t\"model\":      t.modelID,\n\t\t\"provider\":   t.providerName,\n\t})\n\n\taudioFile, err := os.Open(audioFilePath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open audio file %s: %w\", audioFilePath, err)\n\t}\n\tdefer audioFile.Close()\n","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/asr/whisper_transcriber.go#L109-L145","documentation":"Thrown when writer.Close() fails in WhisperTranscriber's multipart builder — the final boundary write to the in-memory bytes.Buffer. Only reachable through allocation failure; if it ever fires without memory pressure, the bytes.Buffer is in an inconsistent state after an earlier partial error. After this point doRequest is skipped, so no network call happens.","triggerScenarios":"Memory exhaustion while finalizing the multipart body.","commonSituations":"Large recordings in memory-capped environments, together with 314/315/316.","solutions":["Apply the memory remediation of 314–316.","If it recurs with small payloads, capture a heap profile and file a bug."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if len(data) > maxInMemoryAudio { return errors.New(\"audio too large; final multipart write may fail under memory pressure\") }","typeGuard":null,"tryCatchPattern":"if _, err := whisper.TranscribeWithData(ctx, data, name); err != nil {\n    // Writer.Close boundary write failed: OOM-class; retry only with smaller input or higher limits.\n    return err\n}","preventionTips":["Treat any multipart-build error on small payloads as a canary: capture a heap profile.","Pre-grow the request buffer for large blobs."],"tags":["memory","multipart","whisper","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}