rust-lang/rust · critical
Unexpected external source {src:?}
Error message
Unexpected external source {src:?} What it means
While encoding a span, rustc_metadata checks whether the span's `SourceFile` is imported (foreign). For foreign files it reads `source_file.external_src` expecting the `ExternalSource::Foreign { metadata_index, .. }` variant; any other variant (e.g. `Local`, `Absent`, or not-yet-loaded) triggers `panic!("Unexpected external source {src:?}")`. This invariant — imported file must have a resolved foreign metadata index — is asserted at encoder.rs:319.
Source
Thrown at compiler/rustc_metadata/src/rmeta/encoder.rs:319
// In `encode_source_map`, we serialize foreign `SourceFile`s into our metadata
// if we're a proc-macro crate.
// This allows us to avoid loading the dependencies of proc-macro crates: all of
// the information we need to decode `Span`s is stored in the proc-macro crate.
let (kind, metadata_index) = if source_file.is_imported() && !s.is_proc_macro {
// To simplify deserialization, we 'rebase' this span onto the crate it originally came
// from (the crate that 'owns' the file it references. These rebased 'lo' and 'hi'
// values are relative to the source map information for the 'foreign' crate whose
// CrateNum we write into the metadata. This allows `imported_source_files` to binary
// search through the 'foreign' crate's source map information, using the
// deserialized 'lo' and 'hi' values directly.
//
// All of this logic ensures that the final result of deserialization is a 'normal'
// Span that can be used without any additional trouble.
let metadata_index = {
// Introduce a new scope so that we drop the 'read()' temporary
match &*source_file.external_src.read() {
ExternalSource::Foreign { metadata_index, .. } => *metadata_index,
src => panic!("Unexpected external source {src:?}"),
}
};
(SpanKind::Foreign, metadata_index)
} else {
// Record the fact that we need to encode the data for this `SourceFile`
let source_files =
s.required_source_files.as_mut().expect("Already encoded SourceMap!");
let (metadata_index, _) = source_files.insert_full(source_file_index);
let metadata_index: u32 =
metadata_index.try_into().expect("cannot export more than U32_MAX files");
(SpanKind::Local, metadata_index)
};
// Encode the start position relative to the file start, so we profit more from the
// variable-length integer encoding.
let lo = self.lo - source_file.start_pos;View on GitHub (pinned to 22057b88b0)
Solutions
- If you hit this as a user (not hacking rustc), it is a compiler ICE — run `cargo clean` and rebuild; if it reproduces, file a rustc issue with the `RUST_BACKTRACE=1` output.
- For rustc hackers: ensure the foreign `SourceFile`'s `external_src` is populated to `ExternalSource::Foreign { metadata_index, source_map }` before encoding spans, or that `is_proc_macro` correctly routes imported files through the local encode_source_map path.
- Audit recent changes to `SourceFile::is_imported` / `encode_source_map` / proc-macro handling to find the missing population of `external_src`.
Example fix
// Conceptual fix in rustc_metadata source-map wiring:
// before: file marked imported but external_src left as Local/Absent
// after: populate external_src before span encoding
//
// source_file.external_src = RwLock::new(ExternalSource::Foreign {
// metadata_index,
// source_map: foreign_source_map.clone(),
// }); Defensive patterns
Strategy: validation
Validate before calling
// The encoder panics on a source kind it doesn't recognise
// ('Unexpected external source'). If you are feeding sources into the encoder
// (e.g. a custom build hook or a proc-macro that emits rmeta), validate the
// source kind against the allow-list BEFORE encoding.
#[derive(Debug)]
enum SourceKind { Crate, Dependency, ExternalCrate /* … */ }
fn is_known_source(src: &SourceKind) -> bool {
matches!(src, SourceKind::Crate | SourceKind::Dependency
| SourceKind::ExternalCrate)
}
// Pre-check every source you hand the encoder:
for src in &sources {
if !is_known_source(src) {
return Err(format!("unexpected external source {src:?}; refusing to encode"));
}
} Type guard
// Narrow the source to a known variant before passing to the encoder.
enum EncoderSource {
Local,
ExternPreloaded,
ExternMacros,
}
fn classify(src: &SourceKind) -> Option<EncoderSource> {
match src {
SourceKind::Crate => Some(EncoderSource::Local),
SourceKind::Dependency => Some(EncoderSource::ExternPreloaded),
SourceKind::ExternalCrate=> Some(EncoderSource::ExternMacros),
_ => None,
}
}
if let Some(kind) = classify(&src) { encoder.encode_source(kind); } Prevention
- Only feed source kinds the encoder documents — anything else is an internal rustc bug, not a user path.
- If you hit this, the cause is almost always a mismatched rustc_internal / rustc_data_structures version in your fork; rebuild against a consistent toolchain.
- File a rustc issue with the `SourceKind` debug print — the panic message exists precisely so unidentified kinds get reported.
When it happens
Trigger: Encoding metadata for a crate whose span references a `SourceFile` flagged `is_imported()` (foreign) but whose `external_src` has not been populated with `ExternalSource::Foreign { .. }` — e.g. the foreign crate's source map was never loaded/encoded, or the proc-macro fast path was taken incorrectly.
Common situations: An internal rustc change that marks a source file imported without wiring up the foreign source map encoding. Building a proc-macro crate where `is_proc_macro` interaction with `is_imported()` misroutes the file into the foreign branch. Span/source-map refactoring that leaves `external_src` in `Local`/`Absent` state at encode time.
Related errors
- Already encoded SourceMap!
- decode error: {e}
- unsupported integer: {self:?}
- unsupported float: {self:?}
- `homogeneous_aggregate` should not be called for scalable ve
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/80ac4bbfa39f95e7.json.
Report an issue: GitHub.