Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: Error Propagation with tryCatch


Francisco Lima (Jul 15 2025 at 12:39):

I am making a automatic theorem solver, as such i need to loop over all available goals the best way I found yet has been to use a try catch with the getMainGoal but as shown in the following example it does not work, the catch pickups the error but the encompassing scope also receives the error for some reason, and only happens once as shown in the second case, so it only happens to the last goal

import Lean

open Lean Elab Meta Tactic

partial def loop (n := 0) : TacticM String :=
  withMainContext do
    try
      let goal  getMainGoal
      admitGoal goal
      let rest  loop (n + 1)
      return s!"sorry\n{rest}"
    catch e =>
      Lean.logInfo m!"Error: {e.toMessageData}"
      return ""

syntax (name := endSorry) "sorry?" : tactic

elab_rules : tactic
  | `(tactic| sorry?) => do
    let raw  loop
    Lean.logInfo raw
    return

theorem test : True := by
  sorry?

theorem test2 : a  b  a  b := by
  intro a
  intro b
  apply And.intro
  sorry?

Aaron Liu (Jul 15 2025 at 12:42):

Well if you logInfo the error obviously it's going to show up

Aaron Liu (Jul 15 2025 at 12:43):

what did you expect to happen

Francisco Lima (Jul 15 2025 at 12:44):

Aaron Liu said:

Well if you logInfo the error obviously it's going to show up

the log is there to see it happens, but if the line is commented out the problem still happens (was happening before I started logging the error)

Aaron Liu (Jul 15 2025 at 12:45):

what's the problem you are experiencing

Francisco Lima (Jul 15 2025 at 12:46):

partial def loop (n := 0) : TacticM String :=
  withMainContext do
    try
      let goal  getMainGoal
      admitGoal goal
      let rest  loop (n + 1)
      return s!"sorry\n{rest}"
    catch e =>
      -- Lean.logInfo m!"Error: {e.toMessageData}"
      return "error"

still gives the same 'error', that is, does not close with sorry the last goal

Aaron Liu (Jul 15 2025 at 12:47):

it seems to me that all the goals are closed though?

Aaron Liu (Jul 15 2025 at 12:47):

you can put done at the end of your by block and it doesn't complain

Francisco Lima (Jul 15 2025 at 12:48):

image.png

Francisco Lima (Jul 15 2025 at 12:48):

has the same error, does not close the last goal

Aaron Liu (Jul 15 2025 at 12:48):

interesting

Aaron Liu (Jul 15 2025 at 12:52):

if you return "" instead of return "error" I don't get a problem

Francisco Lima (Jul 15 2025 at 13:00):

In the actual code I do a return "" and the problem is still there

Francisco Lima (Jul 15 2025 at 13:05):

The content os the string, not doing some sort of throw should not matter for the final result

Francisco Lima (Jul 15 2025 at 15:35):

don't know if it matters but the problem doesn't seem to be connected with the try/catch since the following code has another error

import Lean

open Lean Elab Meta Tactic

partial def loop (n := 0) : TacticM String :=
  withMainContext do
    let goals  getUnsolvedGoals
    match goals with
    | [] => return ""
    | goal::_ =>
      admitGoal goal
      let rest  loop (n + 1)
      return s!"sorry\n{rest}"

syntax (name := endSorry) "sorry?" : tactic

elab_rules : tactic
  | `(tactic| sorry?) => do
    let raw  loop
    Lean.logInfo raw
    return

theorem test : True := by
  sorry?
  done

theorem test2 : a  b  a  b := by
  intro a
  intro b
  apply And.intro
  sorry?
  done

Damiano Testa (Jul 15 2025 at 15:40):

Shouldn't loop call itself at n - 1, rather than n + 1? Also, if n = 0, you should exit "manually".

Francisco Lima (Jul 15 2025 at 16:00):

n in this case does not matter for the recursion or not, I am using it to keep track here of how deep it has gone, in the actual code it increases as it goes along to keep track of variable naming (crude solution but it works)

Francisco Lima (Jul 15 2025 at 16:02):

with a bit more of logging I discovered it throws the error before the getMainGoal call (or at least it supresses the log)

import Lean

open Lean Elab Meta Tactic

partial def loop (n := 0) : TacticM String :=
  withMainContext do
    try
      Lean.logInfo s!"Pre {n}"
      let goal  getMainGoal
      admitGoal goal
      let rest  loop (n + 1)
      return s!"sorry {n}\n{rest}"
    catch e =>
      -- Lean.logInfo m!"Error: {e.toMessageData}"
      return s!"error {n}"

syntax (name := endSorry) "sorry?" : tactic

elab_rules : tactic
  | `(tactic| sorry?) => do
    let raw  loop
    Lean.logInfo raw
    return

theorem test {a : Prop} : a  a := by
  intro a
  sorry?
-- Expect:
  -- sorry 0
  -- error 1
  done

theorem test2 : a  b  a  b := by
  intro a
  intro b
  apply And.intro
  sorry?
-- Expect:
  -- sorry 0
  -- sorry 1
  -- error 2
  done

Last updated: Dec 20 2025 at 21:32 UTC