{"record":{"id":"7ad53b9113f620a9","repo":"risingwavelabs/risingwave","slug":"should-get-the-first-epoch","errorCode":null,"errorMessage":"should get the first epoch","messagePattern":"should get the first epoch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/common/log_store_impl/kv_log_store/reader.rs","lineNumber":328,"sourceCode":"                            .map_err(|_| anyhow!(\"historical read semaphore closed\"))?,\n                    )\n                } else {\n                    None\n                };\n                KvLogStoreReaderFutureState::ReadStateStoreStream(\n                    self.read_persisted_log_store(range_start).await?,\n                    permit,\n                )\n            };\n        self.rx.rewind(start_offset);\n        Ok(())\n    }\n\n    async fn init(&mut self) -> LogStoreResult<()> {\n        if let Some(init_epoch_rx) = self.init_epoch_rx.take() {\n            let init_epoch = init_epoch_rx\n                .await\n                .map_err(|_| anyhow!(\"should get the first epoch\"))?;\n            let first_write_epoch = init_epoch.curr;\n\n            assert_eq!(\n                self.first_write_epoch.replace(first_write_epoch),\n                None,\n                \"should not init twice\"\n            );\n        } else {\n            let (new_vnode_bitmap, write_epoch) = self\n                .update_vnode_bitmap_rx\n                .recv()\n                .await\n                .ok_or_else(|| anyhow!(\"failed to receive update vnode\"))?;\n            self.state.serde.update_vnode_bitmap(new_vnode_bitmap);\n            self.first_write_epoch = Some(write_epoch);\n        };\n\n        self.future_state = KvLogStoreReaderFutureState::Reset;","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/common/log_store_impl/kv_log_store/reader.rs#L310-L346","documentation":"In the non-vnode-bitmap branch of KvLogStoreReader::init, the reader awaits a oneshot receiver for the first epoch from the writer. The `.await` failing means the writer's oneshot sender was dropped without sending — the writer terminated or was dropped before initializing. Without the first epoch the reader cannot establish its epoch baseline.","triggerScenarios":"Reader `init()` with `init_epoch_rx = Some(...)` whose paired writer dropped `init_epoch_tx` (writer actor terminated, panicked, or was dropped before calling writer init).","commonSituations":"Writer actor failure before first barrier; recovery paths tearing down the writer while the reader still initializes; incorrectly constructed reader/writer pairs in custom log store setups or tests.","solutions":["Check the writer-side logs for a panic/termination before init.","Ensure writer `init()` is called on the paired writer before or concurrently with reader init.","Keep the writer alive until the reader has completed its init handshake.","In tests, call writer.init(epoch) before awaiting reader.init()."],"exampleFix":"// before: writer dropped before sending init epoch\ndrop(writer);\nreader.init().await?;\n\n// after: writer initializes first\nwriter.init(epoch).await?;\nreader.init().await?;","handlingStrategy":"validation","validationCode":"// ensure the writer side is alive before reader init\nassert!(!writer_dropped(), \"paired writer must init before reader\");\nwriter.init(epoch).await?;\nreader.init().await?;","typeGuard":"fn init_handshake_ready(init_epoch_rx: &Option<oneshot::Receiver<EpochPair>>) -> bool { init_epoch_rx.is_some() }","tryCatchPattern":"if let Err(e) = reader.init().await {\n    if e.to_string().contains(\"should get the first epoch\") {\n        // writer died before handshake; rebuild pair\n        rebuild_log_store_pair();\n    }\n    return Err(e);\n}","preventionTips":["Always run writer.init() before reader.init() in the pairing handshake.","Keep the writer alive until the reader completes init.","Check writer-side logs first when this error appears; it is a symptom."],"tags":["channel-closed","init","log-store","streaming"],"backgroundTag":"broken-pipe","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}