rust-lang/rust · critical

expected a `Session`

Error message

expected a `Session`

What it means

This fires inside the `format!` macro expansion in rustc's builtin macros. When emitting the `NAMED_ARGUMENTS_USED_POSITIONALLY` lint diagnostic, the code accesses the diagnostic context's session via `sess.downcast_ref::<rustc_session::Session>().expect(...)`. It expects the diagnostic-providing session to be a `rustc_session::Session`. If the session is a different type (as can happen in non-standard rustc drivers, testing harnesses, or proc-macro servers), the downcast fails and panics.

Source

Thrown at compiler/rustc_builtin_macros/src/format.rs:655

                }
                Width => (span, span),
            };
            let arg_name = args.explicit_args()[index].kind.ident().unwrap();
            ecx.buffered_early_lint.push(BufferedEarlyLint {
                span: Some(arg_name.span.into()),
                node_id: rustc_ast::CRATE_NODE_ID,
                lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY),
                diagnostic: DecorateDiagCompat(Box::new(move |dcx, level, sess| {
                    let (suggestion, name) =
                        if let Some(positional_arg_to_replace) = position_sp_to_replace {
                            let mut name = arg_name.name.to_string();
                            let is_formatting_arg = matches!(used_as, Width | Precision);
                            if is_formatting_arg {
                                name.push('$')
                            };
                            let span_to_replace = if let Ok(positional_arg_content) = sess
                                .downcast_ref::<rustc_session::Session>()
                                .expect("expected a `Session`")
                                .source_map()
                                .span_to_snippet(positional_arg_to_replace)
                                && positional_arg_content.starts_with(':')
                            {
                                positional_arg_to_replace.shrink_to_lo()
                            } else {
                                positional_arg_to_replace
                            };
                            (Some(span_to_replace), name)
                        } else {
                            (None, String::new())
                        };

                    diagnostics::NamedArgumentUsedPositionally {
                        named_arg_sp: arg_name.span,
                        position_label_sp: position_sp_for_msg,
                        suggestion,
                        name,

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. If using a custom rustc driver, ensure your diagnostic handler wraps a real `rustc_session::Session` so the downcast succeeds.
  2. Suppress the lint with `#![allow(named_arguments_used_positionally)]` at crate level to avoid triggering the diagnostic path entirely.
  3. Update to the latest nightly — the diagnostic closure may be refactored to not need the session downcast.
  4. File a bug: the diagnostic should use the provided `sess` generically rather than assuming the concrete `Session` type.

Example fix

// before (triggers the lint → diagnostic path → session downcast)
println!("{0} {x}", "hello", x = "world");

// after (avoid mixing named and positional, silencing the lint)
println!("{0} {1}", "hello", "world");
// or
#![allow(named_arguments_used_positionally)]
Defensive patterns

Strategy: validation

Validate before calling

// As an end user, prevent the lint from firing by avoiding mixed
// named/positional args in format strings:
// Don't do: println!("{0} {x}", val, x = other)
// Do:       println!("{val} {x}", val, x = other)
// Or suppress at crate level:
#![allow(named_arguments_used_positionally)]

Try / catch

// In a custom rustc driver wrapping builtin macros:
let result = std::panic::catch_unwind(|| {
    // expand format! macro here
});
if result.is_err() {
    // session downcast failed; provide a fallback diagnostic
    sess.warn("format args lint unavailable in this driver");
}

Prevention

When it happens

Trigger: Using a `format!` macro where a named argument is also used positionally (triggering the lint), in a context where the diagnostic handler's session is not a `rustc_session::Session` — e.g., a custom rustc driver, a proc-macro server, or a test harness that provides a different session type to the diagnostic emitter.

Common situations: Building custom rustc tooling or drivers that reuse rustc's builtin macro expansion but provide their own session implementation. Also possible with edge cases in the format-args lint machinery after rustc internals refactor the session/diagnostic context type.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/d1c2a97649d1b8d5. Report an issue: GitHub.