{"record":{"id":"6dbbebf3195ce904","repo":"influxdata/influxdb","slug":"unexpected-schema-change","errorCode":null,"errorMessage":"Unexpected schema change","messagePattern":"Unexpected schema change","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/influxdb_iox_client/src/client/flight/mod.rs","lineNumber":76,"sourceCode":"\n    /// Arrow Flight handshake failed.\n    #[error(\"Handshake failed: {0}\")]\n    HandshakeFailed(String),\n\n    /// Serializing the protobuf structs into bytes failed.\n    #[error(transparent)]\n    Serialization(#[from] prost::EncodeError),\n\n    /// Deserializing the protobuf structs from bytes failed.\n    #[error(transparent)]\n    Deserialization(#[from] prost::DecodeError),\n\n    /// Unknown IPC message type.\n    #[error(\"Unknown IPC message type: {0:?}\")]\n    UnknownMessageType(ipc::MessageHeader),\n\n    /// Unexpected schema change.\n    #[error(\"Unexpected schema change\")]\n    UnexpectedSchemaChange,\n}\n\nimpl Error {\n    /// Extracts the underlying tonic status, if any\n    pub fn tonic_status(&self) -> Option<&Status> {\n        if let Self::ArrowFlightError(FlightError::Tonic(status)) = self {\n            Some(status)\n        } else {\n            None\n        }\n    }\n}\n\nimpl From<Status> for Error {\n    fn from(status: Status) -> Self {\n        Self::ArrowFlightError(status.into())\n    }","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/core/influxdb_iox_client/src/client/flight/mod.rs#L58-L94","documentation":"Error variant `UnexpectedSchemaChange` thrown when the Arrow schema observed mid-stream differs from the schema the client established (typically from the initial schema message or a previous batch). Flight result streams must be schema-stable; the client treats any divergence as malformed data.","triggerScenarios":"Streaming query results when a later RecordBatch in the same Flight stream arrives with different fields, types, or field order than the stream's declared schema — e.g. the underlying IOx query returned evolving/changing schemas across batches.","commonSituations":"Queries spanning partitions or tables with altered schema; server-side schema migration occurring while a long-running stream is open; writing a custom query whose output columns change (e.g. via dynamic SQL).","solutions":["Re-run the query to get a fresh, consistent stream","Check whether the source table's schema was altered during the query and re-plan after migration completes","Make the query's output schema deterministic (explicit column list, casts)","Upgrade client/server — some mid-stream schema handling was relaxed/fixed in newer IOx versions"],"exampleFix":"// before\nlet batches = client.query(\"SELECT * FROM measurements\").await?;\n// after\nlet batches = client.query(\"SELECT time, region, value FROM measurements\").await?; // fixed output schema","handlingStrategy":"retry","validationCode":"// verify the query yields a fixed schema:\n// SELECT time, region, value FROM ... instead of SELECT *","typeGuard":"fn is_unexpected_schema_change(e: &Error) -> bool {\n    matches!(e, Error::UnexpectedSchemaChange)\n}","tryCatchPattern":"match client.query(sql).await {\n    Err(Error::UnexpectedSchemaChange) => {\n        // re-issue the query for a fresh consistent stream\n        client.query(sql).await\n    }\n    other => other,\n}","preventionTips":["Use explicit column lists and casts so query output schema is stable","Avoid schema migrations on tables during long-running streams","Re-run queries rather than resuming a stream after a schema change","Monitor for concurrent ALTER operations on queried tables"],"tags":["arrow-flight","schema","streaming","data-consistency"],"backgroundTag":"schema-validation-failed","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"}