{"record":{"id":"f3786d86f440d5ef","repo":"risingwavelabs/risingwave","slug":"previous-fragment-info-for-not-found","errorCode":null,"errorMessage":"previous fragment info for {} not found","messagePattern":"previous fragment info for (.+?) not found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/stream/scale.rs","lineNumber":148,"sourceCode":"/// this function sits on the reschedule path, which only re-renders fragments\n/// that were loaded into the `RescheduleContext`. Fragment creation or deletion\n/// is handled by separate DDL / recovery paths, not by reschedule, so fragments\n/// outside the render set are irrelevant here. In other words, this is a subset\n/// check over the rendered fragments, not a full bidirectional equality check.\npub(crate) fn rendered_layout_matches_current(\n    render_result: &FragmentRenderMap,\n    all_prev_fragments: &HashMap<FragmentId, &InflightFragmentInfo>,\n) -> MetaResult<bool> {\n    let all_rendered_fragments: HashMap<_, _> = render_result\n        .values()\n        .flat_map(|jobs| jobs.values())\n        .flatten()\n        .map(|(fragment_id, info)| (*fragment_id, info))\n        .collect();\n\n    for (fragment_id, rendered_fragment) in &all_rendered_fragments {\n        let Some(prev_fragment) = all_prev_fragments.get(fragment_id).copied() else {\n            return Err(MetaError::from(anyhow!(\n                \"previous fragment info for {} not found\",\n                fragment_id\n            )));\n        };\n\n        let rendered_layout = build_normalized_fragment_layout(rendered_fragment);\n        let current_layout = build_normalized_fragment_layout(prev_fragment);\n\n        if rendered_layout != current_layout {\n            return Ok(false);\n        }\n    }\n\n    Ok(true)\n}\n\npub struct ScaleController {\n    pub metadata_manager: MetadataManager,","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/stream/scale.rs#L130-L166","documentation":"When checking whether a rendered (re-planned) fragment layout matches the current one, `rendered_layout_matches_current` looks up each rendered fragment id in the map of previous fragment states (`all_prev_fragments`). A rendered fragment with no corresponding previous fragment means the render produced fragments unknown to the current job state, so the function fails with this error. It guards the no-shuffle ensemble layout comparison during reschedule planning.","triggerScenarios":"Calling `build_reschedule_from_context` for a job whose render result contains fragment ids absent from `all_prev_fragments` — e.g. fragments added by a schema change/refresh that the previous-state snapshot does not yet include, or passing a stale/incomplete previous-fragment map.","commonSituations":"Rescheduling a job immediately after it entered a new phase (e.g. after a table refresh added fragments) while the reschedule context was built from an older snapshot; parallel schema change and reschedule operations racing.","solutions":["Wait for the in-flight schema change/refresh to finish before issuing the reschedule, then retry.","Rebuild the reschedule context so `all_prev_fragments` reflects the job's current fragment set.","Verify the render result does not introduce new fragment ids for a layout-only reschedule check.","If the fragment legitimately exists but is missing from the map, fix how previous fragments are collected (e.g. include fragments from all ensembles)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure every rendered fragment exists in the previous snapshot before rescheduling\nlet missing: Vec<_> = rendered.keys().filter(|id| !prev_fragments.contains_key(*id)).collect();\nif !missing.is_empty() { return Err(format!(\"stale snapshot, missing fragments: {missing:?}\")); }","typeGuard":null,"tryCatchPattern":"match build_reschedule_from_context(ctx).await {\n    Ok(plan) => apply(plan),\n    Err(e) if e.to_string().contains(\"previous fragment info\") => {\n        // concurrent DDL detected: rebuild context and retry\n        let ctx = rebuild_context().await?;\n        build_reschedule_from_context(ctx).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Avoid rescheduling while a schema change or refresh is in progress on the same job.","Always build the reschedule context from the freshest meta snapshot.","Check for in-flight DDL on the job before issuing a reschedule."],"tags":["reschedule","fragment","scale"],"backgroundTag":"entity-not-found","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}