risingwavelabs/risingwave · error
Should not get split info from incomplete source scan info
Error message
Should not get split info from incomplete source scan info
What it means
`SourceScanInfo::split_info` (src/frontend/src/scheduler/plan_fragmenter.rs:373) returns the splits only when the scan info is in the `Complete` state. Calling it on an `Incomplete` variant returns an `SchedulerError::Internal` because splits have not yet been enumerated — the caller violated the expected state transition (complete the source scan first).
Source
Thrown at src/frontend/src/scheduler/plan_fragmenter.rs:373
impl SourceScanInfo {
pub fn new(fetch_info: SourceFetchInfo) -> Self {
Self::Incomplete(fetch_info)
}
pub async fn complete(self, batch_parallelism: usize) -> SchedulerResult<Self> {
match self {
SourceScanInfo::Incomplete(fetch_info) => fetch_info.complete(batch_parallelism).await,
SourceScanInfo::Unpartitioned(data) => data.complete(batch_parallelism),
SourceScanInfo::Complete(_) => {
unreachable!("Never call complete when SourceScanInfo is already complete")
}
}
}
pub fn split_info(&self) -> SchedulerResult<&Vec<SplitImpl>> {
match self {
Self::Incomplete(_) => Err(SchedulerError::Internal(anyhow!(
"Should not get split info from incomplete source scan info"
))),
Self::Unpartitioned(_) => Err(SchedulerError::Internal(anyhow!(
"Should not get split info from unpartitioned source scan info"
))),
Self::Complete(split_info) => Ok(split_info),
}
}
}
impl UnpartitionedData {
fn complete(self, batch_parallelism: usize) -> SchedulerResult<SourceScanInfo> {
let splits = match self {
UnpartitionedData::Iceberg { task, limit } => {
IcebergScanTaskPlanner::plan_splits(task, batch_parallelism, limit)?
.into_iter()
.map(SplitImpl::Iceberg)
.collect()View on GitHub (pinned to 6469eb736d)
Solutions
- Ensure the source scan info is driven through `complete()` before reading `split_info()`.
- Check why split enumeration failed earlier — fix the underlying connector/split-listing error.
- Match on the enum (`Complete`) in caller code instead of assuming completeness.
- Report if triggered by a stock query — it is a state-machine violation in the scheduler.
Example fix
// before
let splits = source_scan_info.split_info()?;
// after
let splits = match source_scan_info {
SourceScanInfo::Complete(_) => source_scan_info.split_info()?,
_ => return Err(anyhow!("source scan not completed before scheduling")),
}; Defensive patterns
Strategy: type-guard
Type guard
fn is_complete(info: &SourceScanInfo) -> bool {
matches!(info, SourceScanInfo::Complete(_))
} Try / catch
match source_scan_info {
SourceScanInfo::Complete(_) => source_scan_info.split_info()?,
SourceScanInfo::Incomplete(_) => return Err(anyhow!("source scan incomplete; complete() first")),
_ => Default::default(),
} Prevention
- Always call complete() before reading split info.
- Match on the enum instead of assuming Complete.
- Fail fast on connector split-listing errors instead of carrying Incomplete state forward.
When it happens
Trigger: Calling `source_scan_info.split_info()` before the fragmenter finished split enumeration for a partitionable source (e.g. during scheduling of a source scan whose `complete()` step was skipped or failed).
Common situations: Internal scheduling bugs where `IncompleteSourceScanInfo` is carried into split-consuming code paths, e.g. after a connector split-listing failure left the scan info incomplete.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Should not get split info from unpartitioned source scan inf
- The stage has single distribution, but contains a source ope
- table {} is not active
- Snowflake sink committer is not initialized.
- expected RowNumberState, got {other:?}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/ccf23c4a54a5c83f.
Report an issue: GitHub.