vectordotdev/vector · error
a sink must always have an external resource
Error message
a sink must always have an external resource
What it means
This panic fires in Vector's component validation runner when building the external resource for a sink. Every sink under validation is expected to yield a Some(external_resource) from its builder; the runner creates an mpsc channel so the resource can deliver the sink's output events back to the validator. A None here means the sink framework's contract (a sink always exposes an external resource for validation) was broken, so the code aborts via expect rather than proceeding without a way to observe sink output.
Solutions
- Check the sink's builder/validate path and ensure it always constructs and returns an ExternalResource (never None) for sinks.
- Verify you are calling run_validation with a sink config, not a source or transform config.
- If you recently refactored src/components/validation, restore the code path that wires resource.spawn_as_output with the mpsc channel.
Example fix
// before
let resource = maybe_external_resource.expect("a sink must always have an external resource");
// after
let resource = maybe_external_resource.unwrap_or_else(|| {
panic!("sink {} did not provide an external resource for validation", sink_name)
}); Defensive patterns
Strategy: validation
Validate before calling
assert!(sink_resource.is_some(), "sink {:?} must expose an external resource for validation", component_id); Type guard
fn has_external_resource(r: &Option<ExternalResource>) -> bool { r.is_some() } Prevention
- Always construct the ExternalResource in the sink's validation build path, unconditionally.
- Add a unit test asserting every registered sink builder yields Some(external_resource).
- Never route non-sink components through the sink validation path.
When it happens
Trigger: Calling run_validation on a sink whose registered builder returns None for external_resource, or validating an object that is not actually a sink but was passed down the sink validation path. In practice only reachable by adding/modifying a sink whose ExternalResource construction was skipped or made conditional.
Common situations: A developer adds a new sink or refactors sink build code and forgets to construct/return the external resource in the validation-specific build path; a custom sink integration test harness mislabels a source/transform as a sink.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- stream must exist
- a record with a next ID must have an event count
- a source must always have an external resource
- a validated HTTP endpoint is a valid `http 1` URI
- breaking fragment ' ' has an invalid anchor ' '. Add ` }`…
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/f4230ff6a1ed97a3.
Report an issue: GitHub.
Appendix: source
Thrown at src/components/validation/runner/mod.rs:490
Ok((
RunnerInput::External(tx),
RunnerOutput::Controlled,
maybe_encoder,
))
}
ComponentType::Transform => {
// Transforms have no external resources.
Ok((RunnerInput::Controlled, RunnerOutput::Controlled, None))
}
ComponentType::Sink => {
// As an external resource for a sink, we create a channel that the validation runner
// uses to collect the output events from the external resource. We don't care if the sink
// pushes those output events to the external resource, or if the external resource
// pulls them from the sink: we just care about getting them from the external resource.
let (tx, rx) = mpsc::channel(1024);
let resource =
maybe_external_resource.expect("a sink must always have an external resource");
resource.spawn_as_output(
tx,
output_task_coordinator,
test_case.events.clone(),
runner_metrics,
configuration.log_namespace(),
)?;
Ok((
RunnerInput::Controlled,
RunnerOutput::External(rx),
maybe_encoder,
))
}
}
}
View on GitHub (pinned to bdb87aeaa4)