sipeed/picoclaw · error

transcription request failed: %w

Error message

transcription request failed: %w

What it means

Thrown when the underlying provider.Chat call fails while sending the base64-encoded audio (data:audio/<fmt>;base64,... media attachment) to the configured chat model. This wraps whatever error the providers layer produced: HTTP transport failure, non-2xx API response, model rejection of audio input, or context cancellation. The error is wrapped with %w so errors.As/Is can reach provider-specific error types.

Source

Thrown at pkg/audio/asr/audio_model_transcriber.go:81

	if err != nil {
		logger.ErrorCF("voice", "Failed to detect audio format", map[string]any{"path": audioFilePath, "error": err})
		return nil, err
	}

	resp, err := t.provider.Chat(ctx, []providers.Message{
		{
			Role:    "user",
			Content: t.prompt,
			Media: []string{
				fmt.Sprintf("data:audio/%s;base64,%s", format, base64.StdEncoding.EncodeToString(audioBytes)),
			},
		},
	}, nil, t.modelID, map[string]any{
		"temperature": 0,
	})
	if err != nil {
		logger.ErrorCF("voice", "Audio model transcription request failed", map[string]any{"error": err})
		return nil, fmt.Errorf("transcription request failed: %w", err)
	}

	text := strings.TrimSpace(resp.Content)
	logger.InfoCF("voice", "Audio model transcription completed successfully", map[string]any{
		"text_length":           len(text),
		"transcription_preview": utils.Truncate(text, 50),
	})

	return &TranscriptionResponse{Text: text}, nil
}

func (t *AudioModelTranscriber) Name() string {
	return "audio-model"
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Inspect the wrapped error with errors.As for the provider's HTTP error type to get status code and response body.
  2. Verify the model configured for this transcriber actually accepts audio input media (check supportsAudioTranscription in pkg/audio/asr/asr.go and the model list).
  3. Confirm the API key/base URL in the model config is valid for that protocol.
  4. For transient transport errors (timeout, connection reset), retry with backoff; for 4xx do not retry.
  5. For long audio, switch to ElevenLabsTranscriber or WhisperTranscriber which upload raw files instead of base64 chat payloads.

Example fix

// before
resp, err := t.provider.Chat(ctx, msgs, nil, t.modelID, opts)
if err != nil { return nil, err }

// after (caller side, with retry on transient failures)
var httpErr *api.HTTPError
if errors.As(err, &httpErr) && httpErr.StatusCode >= 500 && httpErr.StatusCode < 600 {
    // transient upstream failure: safe to retry
    return retryWithBackoff(ctx, func() (*TranscriptionResponse, error) {
        return transcriber.Transcribe(ctx, path)
    })
}
Defensive patterns

Strategy: retry

Validate before calling

// Before first use: smoke-test the chat provider with a tiny payload.
if _, err := provider.Chat(ctx, []providers.Message{{Role: "user", Content: "ping"}}, nil, modelID, nil); err != nil {
    return fmt.Errorf("audio transcriber backend unhealthy: %w", err)
}

Type guard

func isRetryableProviderErr(err error) bool {
    var netErr net.Error
    if errors.As(err, &netErr) && netErr.Timeout() { return true }
    if errors.Is(err, context.DeadlineExceeded) { return true }
    var httpErr *api.HTTPError
    return errors.As(err, &httpErr) && httpErr.StatusCode >= 500 && httpErr.StatusCode < 600
}

Try / catch

resp, err := transcriber.Transcribe(ctx, path)
if err != nil {
    if isRetryableProviderErr(err) {
        resp, err = backoff.Retry(ctx, 3, time.Second, func() (*asr.TranscriptionResponse, error) {
            return transcriber.Transcribe(ctx, path)
        })
    }
    if err != nil { return fmt.Errorf("audio-model transcription failed: %w", err) }
}

Prevention

When it happens

Trigger: provider.Chat(ctx, messages, nil, t.modelID, {temperature: 0}) returns an error: network/DNS/TLS failure to the LLM endpoint; 401/403 from an invalid or missing API key; 404 when modelID does not exist; 4xx when the model does not accept audio media parts (only OpenAI-compatible audio models work); 413 when the base64 payload exceeds the gateway limit; ctx cancelled or deadline exceeded.

Common situations: Model config points at a text-only model (e.g. plain gpt-4o instead of an audio-capable variant); API key env var not exported in the service environment; very long recordings inflate the base64 payload past a reverse-proxy body limit; user cancels the turn mid-request.

Related errors


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