vectordotdev/vector · error

Failed type coercion, {self:?} is not a Stream

Error message

Failed type coercion, {self:?} is not a Stream

What it means

into_stream() is the inverse coercion on VectorSink: it unwraps the Stream variant (a boxed StreamSink<EventArray>) and panics if the value is the Sink variant. Like into_sink(), the panic is documented and indicates the caller assumed the wrong sink shape at runtime.

Source

Thrown at lib/vector-core/src/sink.rs:61

    /// # Panics
    ///
    /// This function will panic if the self instance is not `VectorSink::Sink`.
    pub fn into_sink(self) -> Box<dyn Sink<EventArray, Error = ()> + Send + Unpin> {
        match self {
            Self::Sink(sink) => sink,
            _ => panic!("Failed type coercion, {self:?} is not a Sink"),
        }
    }

    /// Converts `VectorSink` into a `StreamSink`
    ///
    /// # Panics
    ///
    /// This function will panic if the self instance is not `VectorSink::Stream`.
    pub fn into_stream(self) -> Box<dyn StreamSink<EventArray> + Send> {
        match self {
            Self::Stream(stream) => stream,
            _ => panic!("Failed type coercion, {self:?} is not a Stream"),
        }
    }

    /// Converts an event sink into a `VectorSink`
    ///
    /// Deprecated in favor of `VectorSink::from_event_streamsink`. See [vector/9261]
    /// for more info.
    ///
    /// [vector/9261]: https://github.com/vectordotdev/vector/issues/9261
    #[deprecated]
    pub fn from_event_sink(sink: impl Sink<Event, Error = ()> + Send + Unpin + 'static) -> Self {
        VectorSink::Sink(Box::new(EventSink::new(sink)))
    }

    /// Converts an event stream into a `VectorSink`
    pub fn from_event_streamsink(sink: impl StreamSink<Event> + Send + 'static) -> Self {
        let sink = Box::new(sink);
        VectorSink::Stream(Box::new(EventStream { sink }))

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Branch on the variant: use into_stream() only for VectorSink::Stream, into_sink() for VectorSink::Sink
  2. Prefer VectorSink::run(input) so the enum is consumed without assuming either shape
  3. If a StreamSink is required downstream, wrap the futures::Sink with an adapter that implements StreamSink instead of coercing

Example fix

// before
let stream_sink = vector_sink.into_stream(); // panics for Sink variant

// after
if matches!(vector_sink, VectorSink::Stream(_)) {
    let stream_sink = vector_sink.into_stream();
} else {
    let fut_sink = vector_sink.into_sink();
}
Defensive patterns

Strategy: type-guard

Validate before calling

if matches!(vector_sink, VectorSink::Stream(_)) {
    let s = vector_sink.into_stream(); // safe
} else {
    let s = vector_sink.into_sink();
}

Type guard

fn is_stream_variant(v: &VectorSink) -> bool {
    matches!(v, VectorSink::Stream(_))
}

Prevention

When it happens

Trigger: Calling sink.into_stream() on a VectorSink built as VectorSink::Sink (e.g. via from_event_sink or a sink adapter wrapping a futures::Sink), usually in stream-driving wrapper code.

Common situations: Wrapper code written for the StreamSink model receiving a legacy or custom sink that wraps futures::Sink; test harnesses that consume every sink as a stream.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/b3447693011461c2. Report an issue: GitHub.