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 ElevenLabsTranscriber.Transcribe. Close writes the final multipart boundary to the bytes.Buffer; like the other WriteField failures it is only reachable through a buffer write/allocation error. If Close is skipped the Content-Type boundary would be invalid, so the code correctly aborts.

Source

Thrown at pkg/audio/asr/elevenlabs_transcriber.go:88

	part, err := writer.CreateFormFile("file", filepath.Base(audioFilePath))
	if err != nil {
		logger.ErrorCF("voice", "Failed to create form file", map[string]any{"error": err})
		return nil, fmt.Errorf("failed to create form file: %w", err)
	}

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

	if err = writer.WriteField("model_id", t.modelID); err != nil {
		return nil, fmt.Errorf("failed to write model_id field: %w", err)
	}

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

	url := t.apiBase + "/v1/speech-to-text"
	req, err := http.NewRequestWithContext(ctx, "POST", url, &requestBody)
	if err != nil {
		logger.ErrorCF("voice", "Failed to create request", map[string]any{"error": err})
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.Header.Set("Xi-Api-Key", t.apiKey)

	logger.DebugCF("voice", "Sending transcription request to ElevenLabs API", map[string]any{
		"url":                url,
		"request_size_bytes": requestBody.Len(),
		"file_size_bytes":    fileInfo.Size(),
	})

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Free memory headroom: cap audio duration/size at intake.
  2. Increase container memory limit.
  3. Reproduce with a stress test; if Close() fails without memory pressure, file a Go runtime issue.
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(path); err == nil && fi.Size() > maxBufferedAudio {
    return errors.New("audio too large; transcription would buffer it fully in RAM")
}

Try / catch

if _, err := el.Transcribe(ctx, path); err != nil {
    // Buffer-write failures at Close(): memory bound issue, retry with a smaller file only.
    return err
}

Prevention

When it happens

Trigger: Memory allocation failure while growing the buffer for the closing boundary; effectively only under severe memory pressure after a large audio file has been buffered.

Common situations: Same as 306: cgroup memory limits vs large recordings; 32-bit builds.

Related errors


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