astral-sh/ruff · error

there must be a first type if there are conflicting types

Error message

there must be a first type if there are conflicting types

What it means

DeclaredTypeAndConflictingTypesBuilder assembles a symbol's declared type plus the list of conflicting declared types recorded across multiple declarations. Its invariant is that whenever a conflicting type is recorded, the original first declared type is stashed in first_type; build() panics if conflicting_types is non-empty while first_type was never set.

Solutions

  1. Minimize the module to the two conflicting annotations that trigger the panic and file a ty issue with it
  2. As a contributor: ensure every path that pushes onto conflicting_types sets first_type beforehand (record the original type before treating later types as conflicts)
  3. Workaround until fixed: unify or remove one of the conflicting declarations for that name
Defensive patterns

Strategy: fallback

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| check_file(&db, file)));
match result {
    Ok(diags) => diags,
    Err(payload) => { log::warn!("ty panicked while declaring {}: {:?}", file, payload); vec![] }
}

Prevention

When it happens

Trigger: A symbol that receives two or more different declared types (for example `x: int` in one branch and `x: str]` in another, or repeated annotations) where the code path that pushed the conflicting type never recorded the first type first - a builder misuse or a missed update when a new conflict-recording path was added.

Common situations: Modules with multiple differently-annotated declarations of the same name (conditional imports, branch-local annotations); contributor changes to place_from_declarations conflict collection.

Related errors


AI-assisted analysis of astral-sh/ruff@5bb508f594 (2026-08-20). Data as JSON: /api/errors/85469f50f6419ad7. Report an issue: GitHub.

Appendix: source

Thrown at crates/ty_python_semantic/src/place.rs:2145

                    false
                } else {
                    // An unconditional implementation shadows preceding overload definitions. A
                    // conditional definition, however, can be only one public possibility among
                    // several, so keep any unrelated queued overloaded function in the union.
                    if reachability.is_always_true()
                        || self.queue.is_some_and(|queued| {
                            let Type::FunctionLiteral(queued_function) = queued else {
                                return false;
                            };
                            let queued_definition = queued_function.last_definition(db);
                            function.contains_definition(db, queued_definition)
                        })
                    {
                        self.queue = None;
                    } else {
                        self.drain_queue();
                    }
                    self.add_to_union(element);
                    true
                }
            }
            _ => {
                self.drain_queue();
                self.add_to_union(element);
                true
            }
        }
    }

    fn build(mut self) -> Type<'db> {
        self.drain_queue();
        self.builder.build()
    }
}

/// Accumulates multiple (potentially conflicting) declared types and type qualifiers,

View on GitHub (pinned to 5bb508f594)