Pi66

merodi
A markdown-based static site generator.
git clone https://git.pi66.xyz/merodi

← back to log

fix: handle missing frame in error reporting

author: pi66
date: 2026-07-02 21:42
hash: a0f816d8b032d433706ad391394e321be0d5f463

Diffstat:

M

src/errors.py
32 ++++++++++++++++++++++--------
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
diff --git a/src/errors.py b/src/errors.py
index 93889e6..587a8d0 100644
--- a/src/errors.py
+++ b/src/errors.py
@@ -10,6 +10,8 @@ def get_user_frame(exc: Exception):
     for frame in reversed(tb):
         if "site-packages" not in frame.filename and "<frozen" not in frame.filename:
             return frame
+    if not tb:
+        return None
     return tb[-1]

 def get_error_line(exc: Exception):
@@ -19,11 +21,13 @@ def get_error_line(exc: Exception):
         if lineno <= len(lines):
             return lines[lineno - 1].strip()
     frame = get_user_frame(exc)
+    if not frame:
+        return None
     return frame.line.strip() if frame.line else None

 def fatal(exc: Exception, message: str):
-    if settings.VERBOSE:
-        frame = get_user_frame(exc)
+    frame = get_user_frame(exc)
+    if settings.VERBOSE and frame:
         print(f"{BLUE(frame.name + '()')} {GRAY('—')} {basename(frame.filename)}:{YELLOW(str(frame.lineno))}")
         print(f"{GRAY('│')}  {frame.line}\n")
     die(message)
@@ -35,6 +39,22 @@ def html_fatal(exc: Exception, message: str) -> str:
     warn(f"{type(exc).__name__}: {exc.message if hasattr(exc, 'message') else exc}")
     line = escape(source_line) if source_line else "<i style='color:#666'>line not available</i>"
     detail = escape(str(exc.message)) if hasattr(exc, 'message') else escape(str(exc))
+
+    if frame is not None:
+        location_block = f"""<div style="background: #2a2a2a; border-radius: 6px; overflow: hidden;">
+                <div style="background: #333; padding: 0.5rem 1rem; display: flex; justify-content: space-between;">
+                    <span style="color: #569cd6;">{escape(frame.name)}()</span>
+                    <span style="color: #888;">{escape(basename(frame.filename))}:{frame.lineno}</span>
+                </div>
+                <pre style="margin: 0; padding: 1rem; color: #ddd; white-space: pre-wrap; border-left: 3px solid #f44; font-size: 1.1rem;">{line}</pre>
+            </div>"""
+    else:
+        location_block = """<div style="background: #2a2a2a; border-radius: 6px; overflow: hidden;">
+                <div style="background: #333; padding: 0.5rem 1rem;">
+                    <span style="color: #888;">no source location available</span>
+                </div>
+            </div>"""
+
     return f"""<html>
     <head><meta charset="UTF-8"></head>
     <body style="font-family: monospace; margin: 0; background: #1e1e1e; color: #ccc; min-height: 100vh; font-size: 18px;">
@@ -44,13 +64,7 @@ def html_fatal(exc: Exception, message: str) -> str:
         <div style="padding: 2rem;">
             <p style="color: #f90; font-size: 1.3rem; margin: 0 0 0.25rem;">{detail}</p>
             <p style="color: #888; font-size: 1.2rem; margin: 0 0 2rem;">{escape(message)}</p>
-            <div style="background: #2a2a2a; border-radius: 6px; overflow: hidden;">
-                <div style="background: #333; padding: 0.5rem 1rem; display: flex; justify-content: space-between;">
-                    <span style="color: #569cd6;">{escape(frame.name)}()</span>
-                    <span style="color: #888;">{escape(basename(frame.filename))}:{frame.lineno}</span>
-                </div>
-                <pre style="margin: 0; padding: 1rem; color: #ddd; white-space: pre-wrap; border-left: 3px solid #f44; font-size: 1.1rem;">{line}</pre>
-            </div>
+            {location_block}
         </div>
     </body>
     </html>"""