{"record":{"id":"3db643e1bd9e8568","repo":"openai/codex","slug":"luna-response-did-not-contain-assistant-output","errorCode":null,"errorMessage":"Luna response did not contain assistant output","messagePattern":"Luna response did not contain assistant output","errorType":"exception","errorClass":"LunaSamplerError","httpStatus":null,"severity":"warning","filePath":"codex-rs/ext/guardian-v2/src/async_scorer/sampler.rs","lineNumber":119,"sourceCode":"    pub reasoning_effort: ReasoningEffort,\n    /// Owning turn identifier used for request attribution.\n    pub turn_id: String,\n}\n\n/// Failures returned while connecting or sampling the Luna model.\n#[derive(Debug, Error)]\npub enum LunaSamplerError {\n    /// The thread's provider or scoped credentials could not be resolved.\n    #[error(\"could not resolve the Luna model provider: {0}\")]\n    Provider(#[source] CodexErr),\n    /// The Responses WebSocket could not be opened or streamed.\n    #[error(\"Luna Responses WebSocket failed: {0}\")]\n    Api(#[source] ApiError),\n    /// The provider's WebSocket connect deadline elapsed.\n    #[error(\"Luna Responses WebSocket connection timed out\")]\n    ConnectionTimeout,\n    /// The response did not contain an assistant text value.\n    #[error(\"Luna response did not contain assistant output\")]\n    MissingOutput,\n    /// The response exceeded the bounded output limit.\n    #[error(\"Luna response exceeded the output limit\")]\n    OutputTooLarge,\n    /// A newer classification replaced this request when the pool was full.\n    #[error(\"Luna request was superseded by a newer classification\")]\n    Superseded,\n}\n\nstruct PooledConnection {\n    connection: ResponsesWebsocketConnection,\n    // The bridge routes by thread ID, so each socket needs its own identity.\n    thread_id: String,\n    expires_at: Instant,\n    auth_changes: Option<tokio::sync::watch::Receiver<u64>>,\n}\n\nstruct ConnectionLease {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/guardian-v2/src/async_scorer/sampler.rs#L101-L137","documentation":"The Luna response reached Completed but accumulated neither an assistant OutputText item nor any output_text deltas — there is no text to classify. A model-behavior anomaly (empty completion), classified non-retryable by the sampler, so callers should degrade rather than loop.","triggerScenarios":"LunaSampler::sample receives a Completed event with zero assistant text: the model returned empty content under the strict JSON schema, or events carried only reasoning/metadata items.","commonSituations":"Output schema too restrictive causing empty completions; responses-lite/WebSocket beta behaving differently for tool-less structured requests; transient model glitch.","solutions":["Treat as 'no classification' and proceed unclassified — guardian degrades gracefully.","Retry the turn once at the caller level; empties are often transient.","If reproducible, loosen the strict output schema slightly (optional fields) and re-test.","Capture raw ResponseEvents to confirm what the model actually emitted."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match sampler.sample(request).await {\n    Err(LunaSamplerError::MissingOutput) => {\n        tracing::warn!(\"luna returned no output; proceeding unclassified\");\n        Classification::none()\n    }\n    r => r?,\n}","preventionTips":["Degrade to unclassified instead of surfacing the error to the user.","Allow at most one caller-level retry; empties are usually transient.","When reproducible, capture raw ResponseEvents to inspect what the model emitted."],"tags":["rust","model-response","guardian","codex"],"backgroundTag":"empty-model-response","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}