Wei-Shaw/sub2api · error
invalid_request_error
invalid_request_error
Error message
request body must be valid JSON
What it means
Thrown while decoding the LiveCall request body as JSON when json.Decoder.Decode fails (code=invalid_request_error). Covers syntax errors (trailing commas, truncation), wrong content type with raw body, and completely empty bodies on non-multipart requests. The second decode (single-object check) has its own error.
Source
Thrown at backend/internal/handler/openai_live.go:126
c.Header("Location", liveSidebandLocation(c.FullPath(), created.CallID))
c.Data(http.StatusOK, "application/sdp", created.SDP)
}
func parseLiveCallRequest(c *gin.Context) (*service.LiveCallRequest, error) {
contentType := strings.ToLower(c.GetHeader("Content-Type"))
if strings.HasPrefix(contentType, "multipart/form-data") {
sdp := c.PostForm("sdp")
session := json.RawMessage(c.PostForm("session"))
request := &service.LiveCallRequest{SDP: sdp, Session: session}
if err := service.ValidateLiveCallRequest(request); err != nil {
return nil, err
}
return request, nil
}
var request service.LiveCallRequest
decoder := json.NewDecoder(c.Request.Body)
if err := decoder.Decode(&request); err != nil {
return nil, errors.New("request body must be valid JSON")
}
if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
return nil, errors.New("request body must contain one JSON object")
}
if err := service.ValidateLiveCallRequest(&request); err != nil {
return nil, err
}
return &request, nil
}
func liveSidebandLocation(fullPath, callID string) string {
prefix := "/v1/live/"
if strings.HasPrefix(fullPath, "/backend-api/codex/") {
prefix = "/backend-api/codex/"
}
return prefix + url.PathEscape(callID)
}
View on GitHub (pinned to 073e92d171)
Solutions
- Validate the body is syntactically valid JSON before sending (jq, JSON.stringify)
- Ensure Content-Type is application/json and the full body is transmitted (check for truncation on large SDP payloads)
- Build the payload with a JSON serializer rather than string concatenation
Example fix
// before
fetch(url, { body: `{sdp: ${sdp}}` }) // invalid JSON
// after
fetch(url, { headers: {'Content-Type':'application/json'}, body: JSON.stringify({ sdp }) }) Defensive patterns
Strategy: validation
Validate before calling
// TS: serialize + parse round-trip before sending
const bodyText = JSON.stringify(payload);
JSON.parse(bodyText); // throws locally if the serializer produced bad JSON
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: bodyText }); Type guard
function isLiveCallRequest(v: unknown): v is { sdp?: string; session?: unknown } {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Prevention
- Always build JSON with a serializer, never string templates
- Send Content-Type: application/json so the raw-JSON branch is taken
- For large SDP offers, verify the client is not truncating the body
When it happens
Trigger: POST to /v1/live/... with a malformed JSON body: truncated payload, single quotes, comments, empty body, or body sent as form fields without multipart content type.
Common situations: Clients string-interpolating JSON; proxies truncating large SDP offers; sending x-www-form-urlencoded fields where raw JSON is expected; debug tools posting invalid JSON.
Related errors
- 空 JSON 内容
- proxies is required
- accounts is required
- ChatGPT DeviceCheck returned a malformed attestation
- HTTP error! status: ${response.status}
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/533edd29e8a3968b.
Report an issue: GitHub.