rust-lang/rust · critical
Already encoded SourceMap!
Error message
Already encoded SourceMap!
What it means
During span encoding, local `SourceFile`s are registered into `EncodeContext::required_source_files` (an `IndexMap`) so each gets a stable metadata index. That map is `Option` and is taken/set to `None` once `encode_source_map` has already serialized the full SourceMap. If a later span encode then hits the local branch, `s.required_source_files.as_mut().expect("Already encoded SourceMap!")` panics — a span was encoded after the source map was finalized.
Source
Thrown at compiler/rustc_metadata/src/rmeta/encoder.rs:327
// 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;
// Encode length which is usually less than span.hi and profits more
// from the variable-length integer encoding that we use.
let len = self.hi - self.lo;
let tag = SpanTag::new(kind, ctxt, len.0 as usize);
tag.encode(s);
if tag.context().is_none() {View on GitHub (pinned to 22057b88b0)
Solutions
- As a user: treat as an ICE — `cargo clean`, rebuild, and if reproducible report to rustc with `RUST_BACKTRACE=1`.
- For rustc hackers: move `encode_source_map` to run strictly after all span-encoding sites, or refactor `required_source_files` so local files can be registered lazily even post-finalization (e.g. defer the `take()`).
- Add an assertion/test that no `Span::encode` runs after `EncodeContext::encode_source_map`, and audit the call ordering in `encode_crate` for the offending late span encode.
Example fix
// Conceptual ordering fix in rmeta encoder: // before: encode_source_map(...) runs, then a later span encode panics // after: ensure ALL span-bearing structures are encoded first // // encode_all_definitions(&mut ec); // encodes spans into required_source_files // encode_source_map(&mut ec); // now safe to finalize + take()
Defensive patterns
Strategy: validation
Validate before calling
// The encoder panics if SourceMap is encoded twice in one session
// ('Already encoded SourceMap!'). Pre-track encoding state and refuse the
// second call rather than letting the encoder panic.
struct EncoderState {
source_map_encoded: bool,
}
impl EncoderState {
fn encode_source_map(&mut self) -> Result<(), &'static str> {
if self.source_map_encoded {
return Err("SourceMap already encoded; refusing duplicate pass");
}
self.source_map_encoded = true;
Ok(())
}
}
// Call encode_source_map() exactly once per encoder; this guard makes the
// contract explicit and the failure recoverable. Type guard
// Use the type system to make double-encoding unrepresentable: encoding
// the SourceMap consumes the encoder, returning a 'finalised' type with no
// further source-map method.
struct OpenEncoder<'a> { /* ... */ }
struct FinalisedEncoder<'a> { /* ... */ }
impl<'a> OpenEncoder<'a> {
fn encode_source_map(self) -> FinalisedEncoder<'a> {
// encode once, then drop the open handle so it can't be called again
FinalisedEncoder { /* ... */ }
}
} Prevention
- Treat SourceMap encoding as a one-shot: do it once, at the end of the encoder pass, from a single call site.
- Drive encoding through a state machine (Open -> Finalised) so a second encode is a compile error, not a runtime panic.
- If you fork rustc_metadata, add a debug_assert! at the call site that fails loudly in tests before production hits the panic.
When it happens
Trigger: Calling into span encoding after `encode_source_map` has already consumed `required_source_files` (set it to `None`) — i.e. the encoding pipeline serialized the SourceMap before all spans referencing local files were encoded, or a code path encodes a span out-of-order.
Common situations: A rustc refactor that reorders metadata encoding so the SourceMap is finalized too early. Adding a new rmeta field that lazily encodes spans after `encode_source_map` runs. A proc-macro or incremental-compilation path that encodes spans during/after source-map finalization. For end users this manifests as an ICE.
Related errors
- Unexpected external source {src:?}
- 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/c9eb0780d1d8b8c1.json.
Report an issue: GitHub.