Lean proofs fail in predictable ways. The error messages often indicate a mismatch between the goal and the available assumptions, or a misuse of tactics. Effective debugging is a disciplined process: inspect the goal, inspect the context, and identify the exact mismatch.
Problem
A proof does not compile. Typical symptoms:
tactic failed, no goals solvedtype mismatchunknown identifierfailed to unify
You need a systematic way to locate and fix the issue.
Read the Goal and Context
Always start by inspecting the proof state.
P Q R : Prop
hp : P
hPQ : P -> Q
⊢ QIf the goal is Q and you have hPQ : P -> Q, then Lean expects a proof of P. If you already have hp : P, the fix is immediate:
exact hPQ hpMany errors come from not aligning the goal with available hypotheses.
Mistake: Using assumption Too Early
-- does not work
theorem bad_assumption
(P Q : Prop)
(hp : P)
(hPQ : P -> Q) :
Q := by
assumptionError: no hypothesis has type Q.
Fix:
exact hPQ hpThe tactic assumption does not apply functions.
Mistake: Wrong Rewrite Direction
-- fails
theorem bad_rw
(a b : Nat)
(hAB : a = b)
(hb : b + 1 = 5) :
a + 1 = 5 := by
rw [hAB]
exact hbAfter rw [hAB], the goal becomes b + 1 = 5, but hb already has that type. This case actually works. The real failure happens when direction is reversed incorrectly.
Example:
-- fails
rw [hAB] at hbif the needed direction is opposite.
Fix:
rw [← hAB]
exact hbAlways check which side must change.
Mistake: No Matching Rewrite
-- fails
theorem rw_no_match
(a b c : Nat)
(hAB : a = b) :
c + 1 = c + 1 := by
rw [hAB]There is no occurrence of a in the goal.
Fix: do nothing, or use rfl.
Mistake: Misusing cases
-- incorrect idea
theorem bad_cases
(P Q : Prop)
(hp : P) :
P ∨ Q := by
cases hphp : P is not an inductive type with multiple constructors. There is nothing to split.
Fix:
left
exact hpUse cases only on inductive structures such as P ∨ Q, ∃ x, P x, or data types.
Mistake: Forgetting to Introduce Variables
-- fails
theorem missing_intro
(P Q : Prop) :
P -> Q -> P := by
exact hpError: hp is not in the context.
Fix:
intro hp hq
exact hpThe goal must be transformed before using assumptions.
Mistake: Incorrect Use of apply
-- fails
theorem bad_apply
(P Q : Prop)
(hp : P)
(hPQ : P -> Q) :
Q := by
apply hpapply hp expects hp to have type A -> goal. It does not.
Fix:
apply hPQ
exact hpapply must match the shape of the goal.
Mistake: Forgetting a Subgoal
-- incomplete
theorem incomplete
(P Q : Prop)
(hp : P)
(hq : Q) :
P ∧ Q := by
constructor
· exact hpOne goal remains: ⊢ Q.
Fix:
constructor
· exact hp
· exact hqLean shows remaining goals. Always resolve all of them.
Mistake: Confusing True and False
-- incorrect reasoning
theorem bad_true_false
(P : Prop)
(h : True) :
P := by
cases hThis does not work. True gives no information.
Fix: you cannot derive P from True alone.
Correct pattern:
theorem false_case
(P : Prop)
(h : False) :
P := by
cases hMistake: Misusing Existentials
-- fails
theorem bad_exists
(P : Nat -> Prop)
(h : ∃ n, P n) :
P 0 := by
exact ?You do not know that the witness is 0.
Fix:
rcases h with ⟨n, hn⟩Then use n and hn.
Mistake: Not Using Hypotheses
-- overly complex
theorem unused_hyp
(P Q : Prop)
(hp : P)
(hPQ : P -> Q) :
Q := by
have : Q := by
apply hPQ
exact hp
exact thisThis works, but is unnecessarily indirect.
Better:
exact hPQ hpPrefer direct use unless an intermediate result is reused.
Mistake: Overusing Automation
theorem too_much_automation
(P Q : Prop)
(hp : P)
(hq : Q) :
P ∧ Q := by
aesopThis works, but hides structure.
Prefer:
constructor
· exact hp
· exact hqUse automation when structure is irrelevant.
Debugging Strategy
- Read the goal
- Check if any hypothesis matches it exactly
- If not, check for implications that produce it
- If still not, inspect structure (∧, ∨, ∃, ∀)
- Decide: introduce, destructure, or apply
- If rewriting is needed, check direction and location
Use #check and Hover
Lean allows inspection of terms.
#check hPQ
#check hpThis shows types and helps align reasoning.
In editors, hovering over identifiers shows their types.
Use simp? and Error Messages
simp? suggests simplifications.
Error messages often indicate the mismatch:
type mismatchmeans wrong type usedfailed to unifymeans goal and term differ structurallyunknown identifiermeans missing name
Do not ignore error messages. They describe the failure precisely.
Reduce the Problem
If a proof is complex, isolate the failing part.
have hq : Q := by
-- debug hereWork inside the smaller goal. Once fixed, return to the full proof.
Check Direction of Reasoning
If forward reasoning fails, try backward reasoning.
If exact hQR (hPQ hp) is unclear, rewrite as:
apply hQR
apply hPQ
exact hpChanging perspective often reveals the issue.
Common Patterns
Match goal exactly:
exact hApply implication:
apply hRewrite:
rw [h]Destructure:
rcases h with ⟨x, hx⟩Split cases:
cases hSimplify:
simpCommon Pitfalls
Do not guess tactics blindly. Each step should match the goal shape.
Do not ignore the goal state after each tactic.
Do not assume Lean infers missing steps.
Do not mix forward and backward reasoning without tracking intermediate goals.
Do not let the context grow without purpose.
Takeaway
Debugging is alignment. The goal must match the output of some assumption or construction. When a proof fails, inspect the goal, inspect the context, and identify the mismatch. Then fix it by introducing variables, applying functions, rewriting equalities, or destructuring data.