Billionmail/BillionMail · warning

marshal clone request: %w

Error message

marshal clone request: %w

What it means

BuildCloneRequest marshals the VoiceCloneRequest struct to JSON before constructing the Cartesia clone HTTP request; a json.Marshal failure is wrapped as 'marshal clone request: %w'. In practice json.Marshal on plain data structs is nearly impossible to fail, making this a defensive guard.

Source

Thrown at core/internal/service/video_gen/voice.go:99

		OutputDir: outputDir,
	}
}

// DefaultTTSOutputFormat returns WAV 44.1kHz PCM format.
func DefaultTTSOutputFormat() TTSOutputFormat {
	return TTSOutputFormat{
		Container:  "wav",
		SampleRate: 44100,
		Encoding:   "pcm_f32le",
	}
}

// BuildCloneRequest constructs the HTTP request for voice cloning.
// Exported for testing without making API calls.
func BuildCloneRequest(cfg VoiceConfig, req VoiceCloneRequest) (*http.Request, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("marshal clone request: %w", err)
	}

	httpReq, err := http.NewRequest("POST", cfg.voiceBaseURL()+cartesiaClonePath, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("create clone request: %w", err)
	}

	httpReq.Header.Set("X-API-Key", cfg.APIKey)
	httpReq.Header.Set("Cartesia-Version", cartesiaAPIVersion)
	httpReq.Header.Set("Content-Type", "application/json")
	return httpReq, nil
}

// BuildTTSRequest constructs the HTTP request for text-to-speech.
// Exported for testing without making API calls.
func BuildTTSRequest(cfg VoiceConfig, req TTSRequest) (*http.Request, error) {
	body, err := json.Marshal(req)
	if err != nil {

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Inspect any recently added fields on VoiceCloneRequest for unsupported types (chan, func, cycles) or a faulty MarshalJSON implementation
  2. If a custom MarshalJSON was added, fix it to return valid JSON instead of an error
  3. Validate the request payload in tests (TestBuildCloneRequest_Body covers the happy path) after schema changes

Example fix

// before
type VoiceCloneRequest struct {
    Name    string `json:"name"`
    Samples []byte `json:"samples"`
    // Callback func() `json:"-"` // unmarshalable field causes this error
}
// after
type VoiceCloneRequest struct {
    Name    string `json:"name"`
    Samples []byte `json:"samples"`
    Callback func() `json:"-"` // excluded from JSON with '-'
}
Defensive patterns

Strategy: validation

Validate before calling

func validateCloneRequest(req VoiceCloneRequest) error {
    if req.Name == "" {
        return fmt.Errorf("clone request requires a name")
    }
    if _, err := json.Marshal(req); err != nil {
        return fmt.Errorf("clone request not serializable: %w", err)
    }
    return nil
}

Try / catch

httpReq, err := video_gen.BuildCloneRequest(cfg, req)
if err != nil {
    var jsonErr *json.UnsupportedTypeError
    if errors.As(err, &jsonErr) {
        log.Errorf("unmarshalable field in VoiceCloneRequest: %v", jsonErr)
    }
    return err
}

Prevention

When it happens

Trigger: json.Marshal(req) returns an error, which for this struct realistically only happens if a field is replaced by an unsupported type (e.g. a channel, func, or a cycle introduced via a custom MarshalJSON).

Common situations: Developers extending VoiceCloneRequest with unmarshalable fields (channels, funcs, cyclic pointers, or types with a broken MarshalJSON); normal runtime operation essentially never hits this path.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/d7f151d18ac04a0c. Report an issue: GitHub.