Billionmail/BillionMail · error
decode lipsync status: %w
Error message
decode lipsync status: %w
What it means
After a 200 response, CheckLipSyncStatus decodes the body into LipSyncResponse (ID, Status, VideoURL, Error). This error wraps any json.Decode failure — malformed/truncated JSON, an HTML error page served with status 200, or a connection cut mid-body. It indicates the response was not the expected Sync Labs JSON payload despite the success status code.
Source
Thrown at core/internal/service/video_gen/lipsync.go:156
if err != nil {
return nil, err
}
httpReq = httpReq.WithContext(ctx)
resp, err := cfg.doHTTP(httpReq)
if err != nil {
return nil, fmt.Errorf("lipsync status API call: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("lipsync status API error %d: %s", resp.StatusCode, string(body))
}
var result LipSyncResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decode lipsync status: %w", err)
}
return &result, nil
}
// DownloadLipSyncVideo downloads the completed lip sync video to the output directory.
func DownloadLipSyncVideo(ctx context.Context, cfg LipSyncConfig, videoURL, filename string) (string, error) {
if err := os.MkdirAll(cfg.OutputDir, 0755); err != nil {
return "", fmt.Errorf("create output dir: %w", err)
}
req, err := http.NewRequestWithContext(ctx, "GET", videoURL, nil)
if err != nil {
return "", fmt.Errorf("create download request: %w", err)
}
resp, err := cfg.doHTTP(req)
if err != nil {
return "", fmt.Errorf("download lipsync video: %w", err)View on GitHub (pinned to fc36c76c05)
Solutions
- Log the raw response body (status 200 path) to see what was actually returned before decoding.
- If BaseURL points at a test mock, fix the mock to return valid LipSyncResponse JSON.
- Retry transient decode failures — truncation is often network-related and succeeds on the next poll.
- Check for proxies/interceptors between the service and the API that could rewrite the body.
Example fix
// before: decode error loses the body context
var result video_gen.LipSyncResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
// after: capture body bytes for diagnostics, then decode
body, _ := io.ReadAll(resp.Body)
var result video_gen.LipSyncResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("decode lipsync status: %w (body: %q)", err, string(body))
} Defensive patterns
Strategy: retry
Validate before calling
// inspect response before trusting decode (mocks/proxies)
// ensure test BaseURL stubs return application/json bodies like {"id":"...","status":"pending"} Type guard
func looksLikeJSON(body []byte) bool {
t := bytes.TrimSpace(body)
return len(t) > 0 && (t[0] == '{' || t[0] == '[')
}
func validLipSyncStatus(r *video_gen.LipSyncResponse) bool {
return r != nil && r.Status != ""
} Try / catch
status, err := video_gen.CheckLipSyncStatus(ctx, cfg, jobID)
if err != nil {
if strings.HasPrefix(err.Error(), "decode lipsync status") {
// transient/truncated or non-JSON 200: retry after short delay,
// and dump the raw body for diagnosis on repeated failure
}
return err
}
if !validLipSyncStatus(status) { return errors.New("empty lipsync status payload") } Prevention
- Make test mocks return structurally valid LipSyncResponse JSON.
- Bypass or configure proxies for api.synclabs.so in production networks.
- Retry decode failures a limited number of times before surfacing.
- Log raw bodies on decode errors to distinguish truncation from HTML pages.
When it happens
Trigger: A BaseURL override (test mock) returning empty or HTML bodies with 200; a proxy/captive portal intercepting api.synclabs.so; server returning partial JSON due to premature connection close; response Content-Type changed by an intermediary.
Common situations: Integration tests where the stub returns '{}' or plain text; corporate proxy rewriting responses; transient network truncation during long polls; Sync Labs API version change altering the payload shape (though Decode tolerates missing fields).
Related errors
- decode lipsync response: %w
- marshal lipsync request: %w
- decode clone response: %w
- failed to unmarshal tool call arguments: %v, %s
- error unmarshalling project configuration: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/372232bd2a10f2ba.
Report an issue: GitHub.