sipeed/picoclaw · error

failed to close multipart writer: %w

Error message

failed to close multipart writer: %w

What it means

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.

Source

Thrown at pkg/audio/asr/whisper_transcriber.go:127

	if _, copyErr := io.Copy(part, bytes.NewReader(data)); copyErr != nil {
		logger.ErrorCF("voice", "Failed to copy whisper file content", map[string]any{"error": copyErr})
		return nil, fmt.Errorf("failed to copy file content: %w", copyErr)
	}

	if err = writer.WriteField("model", t.modelID); err != nil {
		logger.ErrorCF("voice", "Failed to write whisper model field", map[string]any{"error": err})
		return nil, fmt.Errorf("failed to write model field: %w", err)
	}

	if err = writer.WriteField("response_format", "json"); err != nil {
		logger.ErrorCF("voice", "Failed to write whisper response_format field", map[string]any{"error": err})
		return nil, fmt.Errorf("failed to write response_format field: %w", err)
	}

	if err = writer.Close(); err != nil {
		logger.ErrorCF("voice", "Failed to close whisper multipart writer", map[string]any{"error": err})
		return nil, fmt.Errorf("failed to close multipart writer: %w", err)
	}

	return t.doRequest(ctx, &requestBody, writer.FormDataContentType(), int64(len(data)))
}

func (t *WhisperTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {
	logger.InfoCF("voice", "Starting whisper transcription", map[string]any{
		"audio_file": audioFilePath,
		"model":      t.modelID,
		"provider":   t.providerName,
	})

	audioFile, err := os.Open(audioFilePath)
	if err != nil {
		return nil, fmt.Errorf("failed to open audio file %s: %w", audioFilePath, err)
	}
	defer audioFile.Close()

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Apply the memory remediation of 314–316.
  2. If it recurs with small payloads, capture a heap profile and file a bug.
Defensive patterns

Strategy: try-catch

Validate before calling

if len(data) > maxInMemoryAudio { return errors.New("audio too large; final multipart write may fail under memory pressure") }

Try / catch

if _, err := whisper.TranscribeWithData(ctx, data, name); err != nil {
    // Writer.Close boundary write failed: OOM-class; retry only with smaller input or higher limits.
    return err
}

Prevention

When it happens

Trigger: Memory exhaustion while finalizing the multipart body.

Common situations: Large recordings in memory-capped environments, together with 314/315/316.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/ab6112f8086be4bc. Report an issue: GitHub.