This is an automated email from the ASF dual-hosted git repository.

hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new bb1ef105e fix(connectors): propagate inner error messages in 
RuntimeError (#4342)
bb1ef105e is described below

commit bb1ef105e54483aea94864c694457f42688cdfa7
Author: Daniil Novikov <[email protected]>
AuthorDate: Wed Sep 30 13:19:31 2026 +0200

    fix(connectors): propagate inner error messages in RuntimeError (#4342)
---
 core/connectors/runtime/src/error.rs               | 50 +++++++++++++++++++---
 .../tests/connectors/runtime/error_isolation.rs    | 48 ++++++++++++++++++++-
 .../connectors/runtime/sink_missing_stream.toml    | 20 +++++++++
 .../stdout_missing_stream.toml                     | 39 +++++++++++++++++
 .../sink_missing_stream_config/stdout_valid.toml   | 39 +++++++++++++++++
 5 files changed, 189 insertions(+), 7 deletions(-)

diff --git a/core/connectors/runtime/src/error.rs 
b/core/connectors/runtime/src/error.rs
index f0f5d11b0..156427b13 100644
--- a/core/connectors/runtime/src/error.rs
+++ b/core/connectors/runtime/src/error.rs
@@ -32,7 +32,7 @@ pub enum RuntimeError {
     FailedToSerializeMessagesMetadata,
     #[error("Failed to serialize raw messages")]
     FailedToSerializeRawMessages,
-    #[error("Connector SDK error")]
+    #[error(transparent)]
     ConnectorSdkError(#[from] iggy_connector_sdk::Error),
     /// A classified state-store failure while loading an enabled source's
     /// state. Process-level: treating it as "no state" would silently rewind
@@ -43,16 +43,20 @@ pub enum RuntimeError {
         connector_key: String,
         source: iggy_connector_sdk::Error,
     },
-    #[error("Iggy client error")]
-    IggyClient(#[from] iggy::prelude::ClientError),
-    #[error("Iggy error")]
+    /// Passed through as-is. An `IggyError` that originated on the server
+    /// loses its fields on the wire (apache/iggy#3735): the client reads
+    /// only the status code and fills the rest with defaults, so ids in the
+    /// message can be zero placeholders and not real ids. This is not
+    /// specific to this variant. It applies wherever a server-originated
+    /// `IggyError` is wrapped or stringified, such as
+    /// `iggy::prelude::ClientError::SdkError` or a message built by a
+    /// connector plugin.
+    #[error(transparent)]
     IggyError(#[from] iggy::prelude::IggyError),
     #[error("Missing Iggy credentials")]
     MissingIggyCredentials,
     #[error("Missing TLS certificate file")]
     MissingTlsCertificateFile,
-    #[error("JSON error")]
-    JsonError(#[from] serde_json::Error),
     #[error("Sink not found with key: {0}")]
     SinkNotFound(String),
     #[error("Sink config not found with key: {0}, version: {1}")]
@@ -94,3 +98,37 @@ impl RuntimeError {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use iggy::prelude::IggyError;
+
+    #[test]
+    fn given_iggy_error_when_displayed_should_include_inner_message() {
+        let inner = IggyError::StreamNameNotFound("orders".to_owned());
+        let expected = inner.to_string();
+
+        let error = RuntimeError::from(inner);
+
+        assert_eq!(
+            error.to_string(),
+            expected,
+            "RuntimeError should carry the IggyError message"
+        );
+    }
+
+    #[test]
+    fn given_connector_sdk_error_when_displayed_should_include_inner_message() 
{
+        let inner = iggy_connector_sdk::Error::InitError("bad 
credentials".to_owned());
+        let expected = inner.to_string();
+
+        let error = RuntimeError::from(inner);
+
+        assert_eq!(
+            error.to_string(),
+            expected,
+            "RuntimeError should carry the SDK error message"
+        );
+    }
+}
diff --git a/core/integration/tests/connectors/runtime/error_isolation.rs 
b/core/integration/tests/connectors/runtime/error_isolation.rs
index a3a899b00..7c918abd4 100644
--- a/core/integration/tests/connectors/runtime/error_isolation.rs
+++ b/core/integration/tests/connectors/runtime/error_isolation.rs
@@ -28,7 +28,8 @@
 //! `source::init` and `sink::init`:
 //!   * plugin path resolution failure (missing `.so`),
 //!   * source state-load failure (unreachable state file),
-//!   * post-container setup failure (invalid duration in stream config).
+//!   * post-container setup failure (invalid duration in stream config),
+//!   * post-container setup failure surfaced as an Iggy error (missing 
stream).
 
 use iggy_common::{Identifier, IggyMessage, MessageClient, Partitioning};
 use iggy_connector_sdk::api::{
@@ -398,3 +399,48 @@ async fn 
given_sink_transform_error_when_batch_processed_should_not_count_filter
     );
     assert_eq!(sink.status, ConnectorStatus::Running);
 }
+
+#[iggy_harness(
+    server(connectors_runtime(
+        config_path = "tests/connectors/runtime/sink_missing_stream.toml"
+    )),
+    seed = seeds::connector_stream
+)]
+async fn 
given_sink_with_missing_stream_when_runtime_starts_should_expose_iggy_error_reason(
+    harness: &TestHarness,
+) {
+    let api_address = harness
+        .connectors_runtime()
+        .expect("connector runtime should be available")
+        .http_url();
+    let http_client = Client::new();
+
+    assert_runtime_healthy(&http_client, &api_address).await;
+    let sinks = fetch_sinks(&http_client, &api_address).await;
+
+    let missing_stream_sink = sinks
+        .iter()
+        .find(|sink| sink.key == "stdout_missing_stream")
+        .expect("Sink with a missing stream should be reported");
+    assert_eq!(missing_stream_sink.status, ConnectorStatus::Error);
+    let last_error = missing_stream_sink
+        .last_error
+        .as_ref()
+        .expect("Sink with a missing stream should expose a last_error");
+    assert!(
+        last_error.message.contains("no_such_stream")
+            && last_error.message.contains("was not found"),
+        "last_error should carry the SDK's StreamNameNotFound message, got: 
{}",
+        last_error.message
+    );
+
+    let valid_sink = sinks
+        .iter()
+        .find(|sink| sink.key == "stdout_valid")
+        .expect("Healthy sibling sink should be reported");
+    assert_eq!(valid_sink.status, ConnectorStatus::Running);
+    assert!(
+        valid_sink.last_error.is_none(),
+        "Healthy sibling sink should have no last_error"
+    );
+}
diff --git a/core/integration/tests/connectors/runtime/sink_missing_stream.toml 
b/core/integration/tests/connectors/runtime/sink_missing_stream.toml
new file mode 100644
index 000000000..842741014
--- /dev/null
+++ b/core/integration/tests/connectors/runtime/sink_missing_stream.toml
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+[connectors]
+config_type = "local"
+config_dir = "tests/connectors/runtime/sink_missing_stream_config"
diff --git 
a/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_missing_stream.toml
 
b/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_missing_stream.toml
new file mode 100644
index 000000000..344b61859
--- /dev/null
+++ 
b/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_missing_stream.toml
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Deliberately broken sink: the configured stream does not exist on the server.
+# Used to assert that the Iggy error reason reaches `last_error` instead of a
+# generic "Iggy error" label.
+
+type = "sink"
+key = "stdout_missing_stream"
+enabled = true
+version = 0
+name = "Stdout missing stream sink"
+path = "../../target/debug/libiggy_connector_stdout_sink"
+verbose = true
+
+[[streams]]
+stream = "no_such_stream"
+topics = ["test_topic"]
+schema = "json"
+batch_length = 100
+poll_interval = "5ms"
+consumer_group = "stdout_missing_stream_sink_connector"
+
+[plugin_config]
+print_payload = true
diff --git 
a/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_valid.toml
 
b/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_valid.toml
new file mode 100644
index 000000000..0670d68fd
--- /dev/null
+++ 
b/core/integration/tests/connectors/runtime/sink_missing_stream_config/stdout_valid.toml
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Healthy companion sink: verifies that a sibling connector with valid
+# configuration still starts and runs when another connector in the same
+# runtime fails initialization.
+
+type = "sink"
+key = "stdout_valid"
+enabled = true
+version = 0
+name = "Stdout valid sink"
+path = "../../target/debug/libiggy_connector_stdout_sink"
+verbose = true
+
+[[streams]]
+stream = "test_stream"
+topics = ["test_topic"]
+schema = "json"
+batch_length = 100
+poll_interval = "5ms"
+consumer_group = "stdout_valid_sink_connector"
+
+[plugin_config]
+print_payload = true

Reply via email to