proofStep discards the message log on failure, so elaboration errors surface only as `internal exception #N`
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
Research direction
Start in REPL/Main.lean at runProofStep and compare its catch branch with the success path's createProofStepReponse and proofState.newMessages handling. Reproduce the failing rw example and verify that proofStep returns the accumulated elaboration diagnostics alongside the error string, while parse errors retain their position information.
Written by the indexing model from the issue text.
Description
Summary
When a tactic fails during elaboration, the real diagnostic is written to the message log and an Exception.internal (the tactic-abort exception) is thrown to unwind. runProofStep's error path converts that exception to a string and never reads the message log, so the diagnostic is lost.
The same tactic through cmd reports the error precisely. The information exists; only the proofStep path drops it.
Reproduction
Lean v4.30.0-rc2, repl at f0a88bf, Mathlib pinned to match.
A — command mode
{"cmd": "theorem probeA (n : Nat) : n + 0 = n := by rw [Nat.totally_made_up_lemma]", "env": 0}
{"env": 1, "messages": [
{"severity": "error", "data": "Unknown constant `Nat.totally_made_up_lemma`"},
{"severity": "error", "data": "unsolved goals\nn : ℕ\n⊢ n + 0 = n"}
]}
B — proofStep mode, same tactic, same environment
{"cmd": "theorem probeB (n : Nat) : n + 0 = n := by sorry", "env": 0}
{"tactic": "rw [Nat.totally_made_up_lemma]", "proofState": 0}
{"message": "Lean error:\ninternal exception #5"}
That is the entire response — no messages, no position.
The same happens for a real lemma applied with arguments that do not unify (e.g. rw [Nat.divisors_prime_pow hp hk] where the hypotheses do not match). A parse error, by contrast, is reported correctly and with a position: rw [ gives <input>:1:4: unexpected end of input; expected ']'.
Where it happens
REPL/Main.lean, runProofStep:
try
let proofState' ← proofState.runString s.tactic
return .inl (← createProofStepReponse proofState' proofState)
catch ex =>
return .inr ⟨"Lean error:\n" ++ ex.toString⟩
The success path calls createProofStepReponse, which collects proofState.newMessages old?. The catch branch returns only ex.toString.
For an Exception.internal that string is necessarily uninformative — Snapshots.lean renders it as "internal exception #" ++ toString id.idx, which is the only thing available, since internal exceptions carry an id and no message by design. The problem is not that rendering; it is that the messages accumulated before the abort are never looked at.
Why it is worth fixing
Consumers that classify prover failures cannot separate "the model named a constant that does not exist" from a genuine internal error, so the bucket reads as harness instability.
Concretely: in a corpus of 2,256 prover attempts I classified every internal exception #5 as infrastructure and reported REPL problems at 12% of errors on one problem source against 3% on another — and proposed investigating the harness. They were model errors, of the single most common kind (hallucinated lemma names). After reclassification, model-attributable errors went from 98.1% to 99.7% and genuine infrastructure failures from 1.9% to 0.3%. Measurement record: https://github.com/haeliotang/lean-corpus-audit/blob/main/findings/F6_repl_error_attribution.json
Any error taxonomy built on proofStep inherits this bias, and it points suspicion at the tooling rather than the model.
Suggested fix
In the catch branch, return the accumulated messages alongside the error string — the same proofState.newMessages the success path already collects. The abort exception itself can keep its current rendering; the logged diagnostics are what callers need.
Happy to test a patch against the reproduction above.
- Dominant language
- Lean
- Stars
- 228
- Forks
- 71
- Avg merge
- 15m
- Merged PRs (30d)
- 4
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from leanprover-community/repl
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
leanprover-community/repl#125 · 1 comment ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
leanprover-community/repl#122 ·
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
leanprover-community/repl#118 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
leanprover-community/repl#115 · 16 comments ·
-
Difficulty 3/5 1-2 days Newbie friendliness 45/100
leanprover-community/repl#107 ·
All issues in leanprover-community/repl
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
from:qa priority:P2 reliability tech-debt
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
spec-kitty/spec-kitty#4874 ·
-
0. Needs triage bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
nextcloud/fulltextsearch#1011 ·
-
automation code-quality cookie deep-report documentation improvement quick-win task-mining
Difficulty 1/5 Under an hour Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100