{"record":{"id":"eebb0aa625701695","repo":"Hmbown/CodeWhale","slug":"lifecycle-outbox-queue-is-full-flush-rejected","errorCode":null,"errorMessage":"lifecycle outbox queue is full; flush rejected","messagePattern":"lifecycle outbox queue is full; flush rejected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/hooks/src/lifecycle_outbox.rs","lineNumber":242,"sourceCode":"    ///\n    /// Ordering: `send` happens before the spawn so events queued before the\n    /// writer starts are drained first, preserving enqueue order. The queue\n    /// is bounded: a wedged consumer drops further events with a warning\n    /// (observability, not control flow) but fails a flush fast instead of\n    /// dropping its reply channel.\n    fn enqueue(self: &Arc<Self>, command: OutboxCommand) -> Result<()> {\n        let is_flush = matches!(command, OutboxCommand::Flush(_));\n        match self.sender.try_send(command) {\n            Ok(()) => {}\n            Err(TrySendError::Full(_)) if !is_flush => {\n                tracing::warn!(\n                    target: \"lifecycle_outbox\",\n                    queue_capacity = OUTBOX_QUEUE_CAPACITY,\n                    \"lifecycle event dropped: outbox queue is full\"\n                );\n            }\n            Err(TrySendError::Full(_)) => {\n                anyhow::bail!(\"lifecycle outbox queue is full; flush rejected\");\n            }\n            Err(TrySendError::Closed(_)) => {\n                anyhow::bail!(\"lifecycle outbox writer task is gone\");\n            }\n        }\n        self.ensure_writer_spawned();\n        Ok(())\n    }\n\n    fn ensure_writer_spawned(self: &Arc<Self>) {\n        if self.writer_spawned.load(Ordering::Acquire) {\n            return;\n        }\n        let _guard = self\n            .spawn_lock\n            .lock()\n            .unwrap_or_else(|poisoned| poisoned.into_inner());\n        if self.writer_spawned.load(Ordering::Acquire) {","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/hooks/src/lifecycle_outbox.rs#L224-L260","documentation":"enqueue pushes lifecycle events onto a bounded mpsc outbox queue (capacity OUTBOX_QUEUE_CAPACITY) and flush is rejected when the queue is full. The library refuses to enqueue (and thus flush) rather than blocking the caller, signaling backpressure as an explicit error.","triggerScenarios":"Calling enqueue while the outbox's bounded channel already holds OUTBOX_QUEUE_CAPACITY pending events — the writer task is slower than producers or is not draining.","commonSituations":"Bursts of lifecycle events overwhelming the writer; the writer task stalled on slow I/O or a blocked downstream; very high event volume in a long-running session.","solutions":["Slow down or batch event production so the writer can drain the queue","Check why the writer task is not draining (stuck I/O, task panic) and restart the component","Increase OUTBOX_QUEUE_CAPACITY if the workload legitimately needs more buffer"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// check queue pressure if the outbox exposes it\nif outbox.approx_pending() >= OUTBOX_QUEUE_CAPACITY - 1 {\n    eprintln!(\"outbox near capacity; coalescing events\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = outbox.enqueue(event).await {\n    if e.to_string().contains(\"queue is full\") {\n        // drop the event (it is already logged) or persist locally and flush later\n        dead_letter_log(event);\n    }\n}","preventionTips":["Coalesce high-frequency lifecycle events before enqueue","Alert when queue occupancy is persistently high","Ensure the writer's downstream sink keeps up with production rate"],"tags":["queue","backpressure","async"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}