{"record":{"id":"5b92d975a18219bb","repo":"risingwavelabs/risingwave","slug":"lhs-buffer-should-not-be-empty","errorCode":null,"errorMessage":"lhs buffer should not be empty ","messagePattern":"lhs buffer should not be empty ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/executor/row_merge.rs","lineNumber":123,"sourceCode":"                    }\n                    AlignedMessage::WatermarkRight(watermark) => {\n                        tracing::warn!(\"unexpected watermark from right stream: {:?}\", watermark);\n                    }\n                }\n            }\n        }\n    }\n\n    #[try_stream(ok = Message, error = StreamExecutorError)]\n    async fn flush_buffers<'a>(\n        data_types: &'a [DataType],\n        lhs_mapping: &'a ColIndexMapping,\n        rhs_mapping: &'a ColIndexMapping,\n        lhs_buffer: &'a mut Vec<StreamChunk>,\n        rhs_buffer: &'a mut Vec<StreamChunk>,\n    ) {\n        if lhs_buffer.is_empty() {\n            bail!(\"lhs buffer should not be empty \");\n        };\n        if rhs_buffer.is_empty() {\n            bail!(\"rhs buffer should not be empty \");\n        };\n\n        for lhs_chunk in lhs_buffer.drain(..) {\n            for rhs_chunk in rhs_buffer.drain(..) {\n                yield Self::build_chunk(\n                    data_types,\n                    lhs_mapping,\n                    rhs_mapping,\n                    lhs_chunk.clone(),\n                    rhs_chunk,\n                )?;\n            }\n        }\n    }\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/executor/row_merge.rs#L105-L141","documentation":"The row-merge executor merges buffered left and right stream chunks pairwise; `flush_buffers` is an internal invariant check that both buffers hold at least one chunk before draining. An empty lhs buffer means the merge was invoked without any left-side data staged, which indicates a scheduling bug rather than user-visible bad input.","triggerScenarios":"Internal: the executor's `execute` path calls `flush_buffers` after receiving messages but never buffered an lhs chunk for the current barrier epoch — e.g. barrier/epoch bookkeeping desync between the two inputs.","commonSituations":"Two-input join-style executors where one side received a barrier before the other; bugs in epoch-based buffering; meta failover replaying barriers out of order.","solutions":["Reproduce with debug logging of per-epoch buffer contents on both inputs and file an issue with the trace — this is an internal invariant violation","Check whether the job uses two upstream fragments of unequal rate and whether recent rescale operations preceded the error; recreate the job as a workaround","Upgrade RisingWave; epoch-buffer desync bugs in row_merge have been fixed across versions","As mitigation, recreate the streaming job so buffer state resets"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match merge_result {\n    Err(e) if e.to_string().contains(\"lhs buffer should not be empty\") => {\n        // Internal epoch-buffering desync: reset executor state by recreating the job\n        recreate_streaming_job(job_id);\n    }\n    other => other?,\n}","preventionTips":["Keep both upstream fragments of a row-merge pipeline healthy and delivering chunks","Test barrier/failover behavior for two-input executors in staging","Track RisingWave releases for row_merge buffering fixes","Alert on jobs that log repeated barrier desyncs before they escalate to this error"],"tags":["rust","streaming","internal-invariant","row-merge"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}