vectordotdev/vector · error

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

Error message

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

What it means

VectorSink is an enum with exactly two shapes: Sink (a boxed futures::Sink<EventArray>) and Stream (a boxed StreamSink<EventArray>). Most modern Vector sinks are built as VectorSink::Stream; into_sink() unwraps the Sink variant and panics on anything else. The panic is documented on the method: it is a programming error to call into_sink() on a stream-shaped sink.

Source

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

    ///
    /// See `VectorSink::run` for errors.
    pub async fn run_events<I>(self, input: I) -> Result<(), ()>
    where
        I: IntoIterator<Item = Event> + Send,
        I::IntoIter: Send,
    {
        self.run(stream::iter(input).map(Into::into)).await
    }

    /// Converts `VectorSink` into a `futures::Sink`
    ///
    /// # 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]

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Use the shape-agnostic VectorSink::run(input) which handles both variants
  2. Match on the enum and call into_sink() only in the VectorSink::Sink arm, using into_stream() otherwise
  3. Guard with matches!(sink, VectorSink::Sink(_)) before unwrapping

Example fix

// before
let sink = vector_sink.into_sink(); // panics for Stream variant

// after
match vector_sink {
    VectorSink::Sink(_) => { let s = vector_sink.into_sink(); /* ... */ }
    VectorSink::Stream(_) => { let s = vector_sink.into_stream(); /* ... */ }
}
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Prevention

When it happens

Trigger: Calling sink.into_sink() on a VectorSink constructed via VectorSink::Stream / from_event_streamsink - typical in generic test utilities or wrapper code that assumes every sink implements futures::Sink.

Common situations: Custom sink wrappers or benches written against the Sink shape while the wrapped sink (e.g. HTTP-based Vector sinks) returns the Stream variant; code migrated from older Vector where more sinks were Sink-shaped.

Related errors


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