{"record":{"id":"f4230ff6a1ed97a3","repo":"vectordotdev/vector","slug":"a-sink-must-always-have-an-external-resource","errorCode":null,"errorMessage":"a sink must always have an external resource","messagePattern":"a sink must always have an external resource","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/components/validation/runner/mod.rs","lineNumber":490,"sourceCode":"\n            Ok((\n                RunnerInput::External(tx),\n                RunnerOutput::Controlled,\n                maybe_encoder,\n            ))\n        }\n        ComponentType::Transform => {\n            // Transforms have no external resources.\n            Ok((RunnerInput::Controlled, RunnerOutput::Controlled, None))\n        }\n        ComponentType::Sink => {\n            // As an external resource for a sink, we create a channel that the validation runner\n            // uses to collect the output events from the external resource. We don't care if the sink\n            // pushes those output events to the external resource, or if the external resource\n            // pulls them from the sink: we just care about getting them from the external resource.\n            let (tx, rx) = mpsc::channel(1024);\n            let resource =\n                maybe_external_resource.expect(\"a sink must always have an external resource\");\n\n            resource.spawn_as_output(\n                tx,\n                output_task_coordinator,\n                test_case.events.clone(),\n                runner_metrics,\n                configuration.log_namespace(),\n            )?;\n\n            Ok((\n                RunnerInput::Controlled,\n                RunnerOutput::External(rx),\n                maybe_encoder,\n            ))\n        }\n    }\n}\n","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/src/components/validation/runner/mod.rs#L472-L508","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet resource = maybe_external_resource.expect(\"a sink must always have an external resource\");\n// after\nlet resource = maybe_external_resource.unwrap_or_else(|| {\n    panic!(\"sink {} did not provide an external resource for validation\", sink_name)\n});","handlingStrategy":"validation","validationCode":"assert!(sink_resource.is_some(), \"sink {:?} must expose an external resource for validation\", component_id);","typeGuard":"fn has_external_resource(r: &Option<ExternalResource>) -> bool { r.is_some() }","tryCatchPattern":null,"preventionTips":["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."],"tags":["validation","sink","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}