{"record":{"id":"391746f31fbc48d5","repo":"dbt-labs/dbt-core","slug":"lowercase-column-names-schema-and-columns-should","errorCode":null,"errorMessage":"lowercase_column_names: schema and columns should be compatible","messagePattern":"lowercase_column_names: schema and columns should be compatible","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/record_batch.rs","lineNumber":245,"sourceCode":"            f.name()\n                .chars()\n                .all(|c| c.is_lowercase() || !c.is_alphabetic())\n        }) {\n            return self;\n        }\n\n        let new_fields: Vec<_> = fields\n            .iter()\n            .map(|f| Arc::new(f.as_ref().clone().with_name(f.name().to_lowercase())))\n            .collect();\n\n        let new_schema = Arc::new(Schema::new_with_metadata(\n            new_fields,\n            schema.metadata().clone(),\n        ));\n\n        RecordBatch::try_new(new_schema, self.columns().to_vec())\n            .expect(\"lowercase_column_names: schema and columns should be compatible\")\n    }\n}\n\npub trait StructArrayExt {\n    /// Looks up a named field in the struct and returns it as a typed array `T`.\n    /// Errors if the field is absent or is not of type `T`.\n    fn column_as<T: 'static>(&self, name: &str) -> AdapterResult<&T>;\n}\n\nimpl StructArrayExt for StructArray {\n    fn column_as<T: 'static>(&self, name: &str) -> AdapterResult<&T> {\n        self.column_by_name(name)\n            .and_then(|c| c.as_any().downcast_ref::<T>())\n            .ok_or_else(|| {\n                AdapterError::new(\n                    AdapterErrorKind::UnexpectedResult,\n                    format!(\"Missing or invalid '{name}' column\"),\n                )","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/record_batch.rs#L227-L263","documentation":"lowercase_column_names rebuilds the RecordBatch with a schema whose field names are lowercased but keeps the original column arrays untouched. Since only names change (not types or lengths), RecordBatch::try_new should always succeed; an Err means the batch was already corrupt (column count/length mismatch vs schema), which is an internal invariant violation and panics via .expect. It is invoked from normalize_result_column_names when normalizing driver result metadata.","triggerScenarios":"Calling lowercase_column_names() (directly or through normalize_result_column_names) on a RecordBatch whose columns do not already match its own schema in count or length — the lowercasing itself never introduces the mismatch.","commonSituations":"A buggy or forked driver that constructs a RecordBatch with mismatched arrays; code that mutated batch columns without rebuilding the schema; arrow version upgrades changing try_new strictness.","solutions":["Validate the incoming batch (schema.fields().len() == batch.num_columns() and each array len == batch.num_rows()) before normalization.","Fix the upstream code/driver that produced the inconsistent RecordBatch; the lowercasing path is not the root cause.","Pin/upgrade arrow-rs to the workspace-pinned version to rule out version-specific behavior.","Report the offending query/driver to the adapter maintainers, since the message marks this as a should-never-happen path."],"exampleFix":"// before\nRecordBatch::try_new(new_schema, self.columns().to_vec())\n    .expect(\"lowercase_column_names: schema and columns should be compatible\")\n// after\nRecordBatch::try_new(new_schema, self.columns().to_vec()).map_err(|e| {\n    AdapterError::new(AdapterErrorKind::Internal, format!(\n        \"lowercase_column_names: incompatible batch: {e}\"))\n})?","handlingStrategy":"validation","validationCode":"fn safe_lowercase(batch: RecordBatch) -> Option<RecordBatch> {\n    let schema = batch.schema();\n    let ok = schema.fields().len() == batch.num_columns()\n        && schema.fields().iter().zip(batch.columns()).all(|(f, c)| c.len() == batch.num_rows());\n    ok.then_some(batch)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify RecordBatch::try_new succeeds at construction time in drivers rather than deferring failure to later transforms","Avoid mutating batch internals without rebuilding via try_new","Pin arrow-rs to the workspace version to avoid stricter/looser try_new checks","Log schema and column lengths when normalization fails to find the producer of the bad batch"],"tags":["arrow","record-batch","panic","column-names"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}