router-for-me/CLIProxyAPI · error

Realtime client secrets require an SDP or JSON call request

Error message

Realtime client secrets require an SDP or JSON call request

What it means

Defensive guard in hostHTTPClient.doHTTP: the method is invoked on a nil client or a client whose host pointer is nil. In practice this means the HTTP bridge object was used after being torn down or was constructed outside the host.

Source

Thrown at internal/client/codex/live/live.go:561

		contentType = "application/json"
	}
	return body, contentType, model, nil
}

func applyClientSecretCallSession(body []byte, contentType, model string, session json.RawMessage) ([]byte, string, string, error) {
	if len(session) == 0 {
		return body, contentType, model, nil
	}
	mediaType, _, errMediaType := mime.ParseMediaType(contentType)
	if errMediaType == nil && (strings.EqualFold(mediaType, "application/sdp") || strings.EqualFold(mediaType, "text/plain")) {
		encoded, errEncode := encodeCallRequest(string(body), session)
		if errEncode != nil {
			return nil, "", "", errEncode
		}
		return encoded, "application/json", modelFromJSON(session), nil
	}
	if errMediaType != nil || !strings.EqualFold(mediaType, "application/json") {
		return nil, "", "", errors.New("Realtime client secrets require an SDP or JSON call request")
	}
	var payload map[string]json.RawMessage
	if errUnmarshal := json.Unmarshal(body, &payload); errUnmarshal != nil {
		return nil, "", "", fmt.Errorf("failed to decode Realtime call request: %w", errUnmarshal)
	}
	payload["session"] = append(json.RawMessage(nil), session...)
	encoded, errMarshal := json.Marshal(payload)
	if errMarshal != nil {
		return nil, "", "", fmt.Errorf("failed to encode Realtime call request: %w", errMarshal)
	}
	return encoded, "application/json", modelFromJSON(session), nil
}

func rewriteCallRequestModel(body []byte, contentType, model string) ([]byte, string, error) {
	upstreamModel := codexRealtimeModel(model)
	mediaType, _, errMediaType := mime.ParseMediaType(contentType)
	if errMediaType != nil || !strings.EqualFold(mediaType, "application/json") || len(bytes.TrimSpace(body)) == 0 {
		return body, upstreamModel, nil

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Obtain the HTTP client from the live host API during a callback, not from a cached global.
  2. Do not issue host HTTP calls after host shutdown has begun.
  3. In tests, construct the bridge through the Host, not as a zero value.
Defensive patterns

Strategy: type-guard

Type guard

// Go: guard before use
if c == nil || c.host == nil {
    return errors.New("host http client is unavailable")
}

Try / catch

resp, err := client.Do(ctx, req)
if err != nil && strings.Contains(err.Error(), "host http client is unavailable") {
    // lifecycle bug: client used outside the host's lifetime — re-acquire from the live host API
}

Prevention

When it happens

Trigger: Calling Do/DoStream on a zero-value hostHTTPClient, or retaining the bridge after the Host was shut down and its back-references dropped.

Common situations: Embedders stashing the plugin-side HTTP client beyond the plugin/host lifecycle; races during shutdown; test code constructing the bridge directly.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/a7ded811bba2e749. Report an issue: GitHub.