vectordotdev/vector · critical
join error or bad poll
Error message
join error or bad poll
What it means
This is Vector's fanout-to-transform task in the topology builder. It pulls input batches, spawns transform futures into in_flight (bounded by the worker-thread-derived TRANSFORM_CONCURRENCY_LIMIT), and forwards outputs. The branch expects Some(Ok(outputs)); anything else - a joined future that panicked (JoinError) or a premature None - trips unreachable!('join error or bad poll'). This panic is the symptom; the root cause is an earlier panic inside a transform task.
Source
Thrown at src/topology/builder.rs:1375
let mut input_rx =
super::ready_arrays::ReadyArrays::with_capacity(input_rx, ready_array_capacity());
let mut in_flight = FuturesOrdered::new();
let mut shutting_down = false;
self.timer_tx.try_send_start_wait();
loop {
tokio::select! {
biased;
result = in_flight.next(), if !in_flight.is_empty() => {
match result {
Some(Ok(mut outputs_buf)) => {
self.send_outputs(&mut outputs_buf).await
.map_err(TaskError::wrapped)?;
}
_ => unreachable!("join error or bad poll"),
}
}
input_arrays = input_rx.next(), if in_flight.len() < *TRANSFORM_CONCURRENCY_LIMIT && !shutting_down => {
match input_arrays {
Some(input_arrays) => {
let mut len = 0;
for events in &input_arrays {
self.on_events_received(events);
len += events.len();
}
let mut t = self.transform.clone();
let mut outputs_buf = self.outputs.new_buf_with_capacity(len);
// Hook CPU-time accounting onto the spawned task at
// the `Future::poll` boundary.
// This is a separate task from the current one, so there is no double-counting.
let task = spawn_timed(View on GitHub (pinned to 3708c39b12)
Solutions
- Scroll up in the logs: with RUST_BACKTRACE=1 the originating panic from the transform task precedes this one and names the real culprit
- Identify the transform via the request span and reproduce the triggering event shape in isolation
- Fix or guard the offending transform (filter/re-route the events that trigger it)
- Upgrade Vector and retest - fanout/concurrency handling improves across releases
Defensive patterns
Strategy: try-catch
Try / catch
// The transform task's panic kills the topology task; supervise the process and alert: // systemd: Restart=on-failure, Environment=RUST_BACKTRACE=1 // Embedding: treat JoinError::is_panic() as a crash-loop with backoff and alerting, // and always pull the FIRST panic in the logs (the transform's own) as root cause.
Prevention
- Always run with RUST_BACKTRACE=1 so the first panic is actionable
- Canary new event sources and transforms in staging with production-shaped data
- Watch internal_metrics for transform task restarts as an early signal
When it happens
Trigger: A spawned transform future panicking while processing events - its JoinError surfaces here - or the futures collection misbehaving (None while non-empty). Real-world event shapes reaching a transform's own panic path (unsupported event types, malformed inputs) are the usual source.
Common situations: A transform hitting one of its own unreachable/panic arms under real traffic (e.g. an unsupported event type or non-standard log format reaching it); high concurrency stressing task spawning; Vector bugs in fanout accounting.
Related errors
- Replacing unknown sink from fanout: {id}
- Pausing unknown sink from fanout: {id}
- concurrent map task cancelled outside of our control
- Traces are not supported.
- source output misconfigured - output for port {:?} missing
AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20).
Data as JSON: /api/errors/7c34f78fb34e8b15.
Report an issue: GitHub.