risingwavelabs/risingwave · critical
should already checked index referencing column still exists
Error message
should already checked index referencing column still exists
What it means
When rewriting an index after ALTER TABLE ADD/DROP COLUMN, rewrite_expr maps an old column index to the new column list by matching column_id. The expect asserts a prior check already verified the referenced column still exists; panicking means an index references a column that was dropped or the column-id mapping is inconsistent.
Solutions
- Verify the index does not reference the column being dropped before issuing ALTER TABLE DROP COLUMN (the check that normally prevents this).
- Re-create the index without the dropped column, or drop the index first.
- If column ids are inconsistent in the meta store, restore from backup / rebuild the table and index.
Defensive patterns
Strategy: validation
Validate before calling
// Before DROP COLUMN, check no index item references it
if indexes_reference_column(table_id, column_id) {
bail!("cannot drop column referenced by an index");
} Try / catch
std::panic::catch_unwind(|| rewrite_expr(expr, &ctx))
Prevention
- Always rely on the pre-check that a referenced column still exists
- Drop dependent indexes before dropping their columns
- Avoid concurrent ALTER TABLE operations on the same table
- Verify column-id mapping consistency after partial ALTER failures
When it happens
Trigger: Renaming/rewriting a table whose index items reference a column absent from new_columns — i.e. the earlier 'index references dropped column' validation was skipped, bypassed, or the column-id bookkeeping is wrong (e.g. after concurrent ALTERs).
Common situations: Dropping a column that an index depends on without the pre-check firing, schema corruption after failed ALTER, concurrent DDL races.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- checked above
- core predicate must exist
- duplicate path
- expect fn
- job fragments should exist for streaming job
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/9314ec507a7dbd70.
Report an issue: GitHub.
Appendix: source
Thrown at src/meta/src/controller/rename.rs:465
/// Rewrite the expression in index item after there's a schema change on the primary table.
// TODO: move this out of `rename.rs`, this has nothing to do with renaming.
pub struct IndexItemRewriter {
pub original_columns: Vec<PbColumnDesc>,
pub new_columns: Vec<PbColumnDesc>,
}
impl IndexItemRewriter {
pub fn rewrite_expr(&self, expr: &mut ExprNode) {
let rex_node = expr.rex_node.as_mut().unwrap();
match rex_node {
RexNode::InputRef(idx) => {
let old_idx = *idx as usize;
let original_column = &self.original_columns[old_idx];
let (new_idx, new_column) = self
.new_columns
.iter()
.find_position(|c| c.column_id == original_column.column_id)
.expect("should already checked index referencing column still exists");
*idx = new_idx as u32;
// If there's a type change, we need to wrap it with an internal `CompositeCast` to
// maintain the correct return type. It cannot execute and will be eliminated in
// the frontend when rebuilding the index items.
if new_column.column_type != original_column.column_type {
let old_type = original_column.column_type.clone().unwrap();
let new_type = new_column.column_type.clone().unwrap();
assert_eq!(&old_type, expr.return_type.as_ref().unwrap());
expr.return_type = Some(new_type); // update return type of `InputRef`
let new_expr_node = ExprNode {
function_type: expr_node::Type::CompositeCast as _,
return_type: Some(old_type),
rex_node: RexNode::FuncCall(FunctionCall {
children: vec![expr.clone()],
})View on GitHub (pinned to 6469eb736d)