Billionmail/BillionMail · error

create status request: %w

Error message

create status request: %w

What it means

BuildLipSyncStatusRequest wraps errors from http.NewRequest when constructing the GET status URL "%s%s/%s" (base + lipSyncPath + jobID). As with [450], http.NewRequest fails only on bad method or unparsable URL, so a malformed base URL is the usual cause; an embedded path segment with invalid characters (often from jobID) also triggers it.

Source

Thrown at core/internal/service/video_gen/lipsync.go:96

	}

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

	httpReq.Header.Set("x-api-key", cfg.APIKey)
	httpReq.Header.Set("Content-Type", "application/json")
	return httpReq, nil
}

// BuildLipSyncStatusRequest constructs the HTTP request to check job status.
// Exported for testing.
func BuildLipSyncStatusRequest(cfg LipSyncConfig, jobID string) (*http.Request, error) {
	url := fmt.Sprintf("%s%s/%s", cfg.lipSyncBase(), lipSyncPath, jobID)
	httpReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("create status request: %w", err)
	}

	httpReq.Header.Set("x-api-key", cfg.APIKey)
	return httpReq, nil
}

// SubmitLipSync submits a lip sync job and returns the job ID.
func SubmitLipSync(ctx context.Context, cfg LipSyncConfig, audioURL, videoURL string) (string, error) {
	req := LipSyncRequest{
		AudioURL:       audioURL,
		VideoURL:       videoURL,
		SynergizeAudio: true,
	}

	httpReq, err := BuildLipSyncRequest(cfg, req)
	if err != nil {
		return "", err
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Validate the base URL has scheme+host at config load time
  2. Sanitize the jobID: trim whitespace and ensure it matches the expected ID format before calling CheckLipSyncStatus
  3. url.PathEscape the jobID (or validate it as alphanumeric) before interpolation
  4. Unwrap the returned error to read url.Parse's specific message

Example fix

// before
resp, err := CheckLipSyncStatus(ctx, cfg, jobIDFromDB) // jobID may contain spaces
// after
jobID = strings.TrimSpace(jobIDFromDB)
if !validJobIDRe.MatchString(jobID) {
    return fmt.Errorf("invalid lipsync jobID %q", jobID)
}
resp, err := CheckLipSyncStatus(ctx, cfg, jobID)
Defensive patterns

Strategy: validation

Validate before calling

jobID = strings.TrimSpace(jobID)
if !regexp.MustCompile(`^[A-Za-z0-9_-]+$`).MatchString(jobID) {
    return fmt.Errorf("invalid lipsync jobID %q", jobID)
}

Type guard

func validJobID(id string) bool {
    id = strings.TrimSpace(id)
    return id != "" && !strings.ContainsAny(id, " /\\?#%{}")
}

Try / catch

req, err := BuildLipSyncStatusRequest(cfg, jobID)
if err != nil {
    return nil, fmt.Errorf("status check aborted for job %q: %w", jobID, err)
}

Prevention

When it happens

Trigger: Calling CheckLipSyncStatus with a LipSyncConfig whose base URL is empty/malformed, or a jobID containing characters that make url.Parse fail (spaces, control chars, raw non-ASCII) since jobID is interpolated into the URL path without escaping.

Common situations: Base URL env var misconfigured the same way as [450]; storing/logging a jobID that included whitespace or a full URL and passing it back unescaped; copy-pasted base URL with trailing newline.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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