astral-sh/ruff · error
Expected `NamedTuple` definition to be an assignment
Error message
Expected `NamedTuple` definition to be an assignment
What it means
For functional NamedTuple syntax (`P = NamedTuple('P', [('x', int)])` resolved through the typing module), ty defers computing the field spec to `deferred_spec`. It re-reads the definition's value node and asserts the definition is an assignment; the expect fires when a `DynamicNamedTupleAnchor::TypingDefinition` points at a definition with no value expression in the module.
Source
Thrown at crates/ty_python_semantic/src/types/class/named_tuple.rs:453
TypeContext::default(),
)
})
}
}
fn spec(self, db: &'db dyn Db) -> NamedTupleSpec<'db> {
#[salsa::tracked(
returns(copy),
cycle_initial=|db, _, _| NamedTupleSpec::unknown(db),
heap_size=ruff_memory_usage::heap_size
)]
fn deferred_spec<'db>(db: &'db dyn Db, definition: Definition<'db>) -> NamedTupleSpec<'db> {
let python_file = definition.python_file(db);
let module = parsed_module(db, python_file).load(db);
let node = definition
.kind(db)
.value(&module)
.expect("Expected `NamedTuple` definition to be an assignment")
.as_call_expr()
.expect("Expected `NamedTuple` definition r.h.s. to be a call expression");
match definition_expression_type(db, definition, &node.arguments.args[1]) {
Type::KnownInstance(KnownInstanceType::NamedTupleSpec(spec)) => spec,
_ => NamedTupleSpec::unknown(db),
}
}
match self.anchor(db) {
DynamicNamedTupleAnchor::CollectionsDefinition { spec, .. }
| DynamicNamedTupleAnchor::ScopeOffset { spec, .. } => *spec,
DynamicNamedTupleAnchor::TypingDefinition(definition) => deferred_spec(db, *definition),
}
}
fn fields(self, db: &'db dyn Db) -> &'db [NamedTupleField<'db>] {
self.spec(db).fields(db)
}View on GitHub (pinned to d1087a4b9e)
Solutions
- Guard at anchor construction: only create `TypingDefinition` for definitions whose `.value()` is Some.
- Add an mdtest for the exact NamedTuple shape that panicked; run the ty_python_semantic mdtests.
- Verify the module is parsed from `definition.python_file(db)`.
- Report with a reproducer if stock ty panics.
Example fix
// before
let node = definition.kind(db).value(&module)
.expect("Expected `NamedTuple` definition to be an assignment");
// after: bail out to the unknown spec instead of panicking
let Some(value) = definition.kind(db).value(&module) else {
return NamedTupleSpec::unknown(db);
}; Defensive patterns
Strategy: try-catch
Try / catch
let spec = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
named_tuple.spec(db)
}))
.unwrap_or_else(|_| NamedTupleSpec::unknown(db)); Prevention
- Use the classic class-based NamedTuple syntax in code under check while a functional-syntax bug is pending.
- Include the exact `NamedTuple(...)` statement in issue reports.
- Contributors: add an mdtest for every new anchor shape you introduce.
When it happens
Trigger: Calling `spec()` on a `DynamicNamedTuple` anchored via `TypingDefinition(definition)` (named_tuple.rs match arm) where `definition.kind(db).value(&module)` is None - a non-assignment definition or one resolved against a different parse.
Common situations: Extending functional-NamedTuple detection (collections vs typing modules, bare call expressions, aliased assignments); refactors of `DefinitionKind`; anchors surviving a reparse in LSP mode.
Related errors
- DynamicClassAnchor::Definition should only be used for assig
- Expected `NamedTuple` definition r.h.s. to be a call express
- dynamic class definitions should only be used for assignment
- anchor should not be NodeIndex::NONE
- string annotation offset should point to ExprStringLiteral
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/32b0846d9de36f93.
Report an issue: GitHub.