{"record":{"id":"b1f3ce8d717b97a9","repo":"influxdata/influxdb","slug":"ordered-catalog-batch-should-succeed-when-applied","errorCode":null,"errorMessage":"ordered catalog batch should succeed when applied","messagePattern":"ordered catalog batch should succeed when applied","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"influxdb3_catalog/src/catalog/versions/v2.rs","lineNumber":709,"sourceCode":"    /// This accepts a `_permit`, which is not used, and is just a way to ensure that the caller\n    /// has a handle on the write permit at the time of invocation.\n    pub(crate) fn apply_ordered_catalog_batch(\n        &self,\n        batch: &OrderedCatalogBatch,\n        _permit: &CatalogWritePermit,\n    ) -> CatalogBatch {\n        let batch_sequence = batch.sequence_number().get();\n        let current_sequence = self.sequence_number().get();\n        assert_eq!(\n            batch_sequence,\n            current_sequence + 1,\n            \"catalog batch received out of order\"\n        );\n        let catalog_batch = self\n            .inner\n            .write()\n            .apply_catalog_batch(batch.batch(), batch.sequence_number(), Some(&self.store))\n            .expect(\"ordered catalog batch should succeed when applied\")\n            .expect(\"ordered catalog batch should contain changes\");\n        self.update_last_check_time();\n        catalog_batch.into_batch()\n    }\n\n    pub fn node(&self, node_id: &str) -> Option<Arc<NodeDefinition>> {\n        self.inner.read().nodes.get_by_name(node_id)\n    }\n\n    pub fn node_by_id(&self, node_id: &NodeId) -> Option<Arc<NodeDefinition>> {\n        self.inner.read().nodes.get_by_id(node_id)\n    }\n\n    pub fn list_nodes(&self) -> Vec<Arc<NodeDefinition>> {\n        self.inner.read().nodes.resource_iter().cloned().collect()\n    }\n\n    pub fn minimum_supported_row_delete_predicate_version(&self) -> Option<usize> {","sourceCodeStart":691,"sourceCodeEnd":727,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/catalog/versions/v2.rs#L691-L727","documentation":"Applying an ordered catalog batch does a double expect: the first asserts apply_catalog_batch never fails for a batch already known to be in sequence order, the second asserts the apply actually produced changes. Either panic means the batch was out of order, malformed, already applied (empty change set), or the underlying catalog/store rejected a batch the ordering check accepted.","triggerScenarios":"Applying a CatalogBatch via the ordered apply path when (a) its sequence number is not the expected next sequence, (b) the batch fails validation inside apply_catalog_batch (bad column types, missing dependencies), or (c) the batch is a duplicate whose application yields no changes, tripping the second expect.","commonSituations":"Catalog replay after a crash with corrupted or duplicated persisted batches; multiple writers to the same catalog producing overlapping sequence numbers; upgrading across versions where persisted batch format changed.","solutions":["Verify batch sequence numbers are strictly sequential with no gaps or duplicates at the producer; fix the writer that assigned them","Inspect the persisted catalog batches around the failing sequence number for duplication or corruption and remove/repair them","Check for multiple processes/nodes writing to the same catalog concurrently; ensure only one writer owns sequence assignment","Reproduce with debug logging of apply_catalog_batch errors to identify the inner failure the expect swallowed","If a batch legitimately applies no changes, adjust the producer to skip empty batches"],"exampleFix":"// before\n.inner.write()\n.apply_catalog_batch(batch.batch(), batch.sequence_number(), Some(&self.store))\n.expect(\"ordered catalog batch should succeed when applied\")\n.expect(\"ordered catalog batch should contain changes\");\n// after\nlet changes = inner\n    .apply_catalog_batch(batch.batch(), batch.sequence_number(), Some(&self.store))\n    .map_err(|e| anyhow!(\"batch seq {} failed to apply: {e}\", batch.sequence_number()))?\n    .ok_or_else(|| anyhow!(\"batch seq {} produced no changes\", batch.sequence_number()))?;","handlingStrategy":"retry","validationCode":"// verify sequence continuity before applying\nlet expected = catalog.last_sequence_number() + 1;\nif batch.sequence_number() != expected {\n    return Err(anyhow!(\"out-of-order batch: got {}, expected {}\",\n        batch.sequence_number(), expected));\n}","typeGuard":"fn is_applicable(batch: &CatalogBatch, expected_seq: u64) -> bool {\n    batch.sequence_number() == expected_seq\n}","tryCatchPattern":"match inner.apply_catalog_batch(batch.batch(), seq, Some(&store)) {\n    Ok(Some(changes)) => Ok(changes),\n    Ok(None) => Err(anyhow!(\"batch {seq} produced no changes; duplicate?\")),\n    Err(e) => Err(anyhow!(\"batch {seq} failed to apply: {e}\")),\n}","preventionTips":["Guarantee a single writer assigns monotonically increasing sequence numbers","Persist and verify batch ordering before catalog replay; repair gaps/duplicates first","Skip or short-circuit batches known to produce no changes","Monitor for concurrent writers to the same catalog store","Log the inner apply error instead of losing it to expect"],"tags":["rust","panic","catalog","replication","ordering"],"backgroundTag":"internal-invariant-violation","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}