router-for-me/CLIProxyAPI · error
invalid_expires_after
invalid_expires_after
Error message
expires_after.anchor must be created_at
What it means
The stream-read callback rejects the call when the receiver Host is nil or its modelStreams bridge is nil. This is a host-side wiring/lifecycle error, not a bad stream ID (a bad ID instead yields 'not open' from the bridge).
Source
Thrown at internal/client/codex/live/client_secret.go:313
c.JSON(http.StatusOK, response)
return
}
c.JSON(http.StatusOK, clientSecretCreateResponse{
Value: token,
ExpiresAt: expiresAt.Unix(),
Session: responseSession,
})
}
func clientSecretLifetime(expiresAfter *struct {
Anchor string `json:"anchor"`
Seconds int64 `json:"seconds"`
}) (time.Duration, error) {
if expiresAfter == nil {
return clientSecretDefaultLifetime, nil
}
if expiresAfter.Anchor != "" && expiresAfter.Anchor != "created_at" {
return 0, errors.New("expires_after.anchor must be created_at")
}
minimumSeconds := int64(clientSecretMinimumLifetime / time.Second)
maximumSeconds := int64(clientSecretMaximumLifetime / time.Second)
if expiresAfter.Seconds < minimumSeconds || expiresAfter.Seconds > maximumSeconds {
return 0, fmt.Errorf("expires_after.seconds must be between %d and %d", minimumSeconds, maximumSeconds)
}
return time.Duration(expiresAfter.Seconds) * time.Second, nil
}
func normalizeClientSecretSession(session json.RawMessage) (json.RawMessage, json.RawMessage, error) {
trimmedSession := strings.TrimSpace(string(session))
if trimmedSession == "" || trimmedSession == "null" {
session = json.RawMessage(`{"type":"realtime","model":"gpt-realtime"}`)
}
var clientSession map[string]any
if errUnmarshal := json.Unmarshal(session, &clientSession); errUnmarshal != nil || clientSession == nil {
return nil, nil, errors.New("session must be a valid JSON object")
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Initialize the Host via the SDK builder so the model stream bridge exists.
- Verify a successful host.model.execute_stream response (non-empty StreamID) before attempting stream_read.
- Treat this error as a programming/setup bug: do not retry, fix host construction.
Defensive patterns
Strategy: validation
Validate before calling
if streamID == "" {
return errors.New("no open stream; cannot read")
} Try / catch
if err != nil && strings.Contains(err.Error(), "stream bridge is unavailable") {
// host-side wiring failure; not retryable from the plugin
return fmt.Errorf("host stream bridge missing: %w", err)
} Prevention
- Only issue stream_read after a successful execute_stream returned a StreamID.
- Build the Host with the full SDK builder.
- Treat bridge-unavailable as a setup bug, not a runtime condition.
When it happens
Trigger: Calling host.model.stream_read on a Host that was never given a model stream bridge, or invoking the RPC method on a zero-value/partially constructed Host.
Common situations: Test harnesses building a Host by hand; embedding code that skips bridge initialization; calling into a Host that is mid-shutdown.
Related errors
- invalid_realtime_client_secret
- count must be a positive integer
- Codex live multipart body requires an sdp field
- Codex live call request requires an SDP offer
- core auth manager unavailable
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/bf32e8eab8501aa3.
Report an issue: GitHub.