diff --git a/bindings/bindings.nim b/bindings/bindings.nim
index 9a120e9..4486c54 100644
--- a/bindings/bindings.nim
+++ b/bindings/bindings.nim
@@ -38,8 +38,7 @@ proc parseColor(s: string): Color {.raises: [PixieError]} =
   try:
     result = parseHtmlColor(s)
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
 
 proc drawImage2(
   ctx: Context, image: Image, dx, dy, dWidth, dHeight: float32
diff --git a/experiments/svg_cairo.nim b/experiments/svg_cairo.nim
index 842e4c2..916b01d 100644
--- a/experiments/svg_cairo.nim
+++ b/experiments/svg_cairo.nim
@@ -79,8 +79,7 @@ proc initCtx(): Ctx =
     result.fill = parseHtmlColor("black").rgbx
     result.stroke = parseHtmlColor("black").rgbx
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
   result.strokeWidth = 1
   result.transform = mat3()
   result.strokeMiterLimit = defaultMiterLimit
@@ -340,8 +339,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
   except PixieError as e:
     raise e
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
 
 proc cairoLineCap(lineCap: LineCap): cairo.LineCap =
   case lineCap:
@@ -526,8 +524,7 @@ proc draw(img: ptr Context, node: XmlNode, ctxStack: var seq[Ctx]) =
   except PixieError as e:
     raise e
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
 
 proc decodeSvg*(data: string, width = 0, height = 0): Image =
   ## Render SVG file and return the image. Defaults to the SVG's view box size.
diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim
index 4b421c8..f786db8 100644
--- a/src/pixie/fileformats/svg.nim
+++ b/src/pixie/fileformats/svg.nim
@@ -1,8 +1,7 @@
 ## Load SVG files.
 
-import chroma, pixie/common, pixie/images, pixie/paints, pixie/paths, strutils,
-    tables, vmath, xmlparser, xmltree
-
+import chroma, pixie/common, pixie/images, pixie/internal, pixie/paints,
+    pixie/paths, strutils, tables, vmath, xmlparser, xmltree
 when defined(pixieDebugSvg):
   import strtabs
 
@@ -44,8 +43,7 @@ proc initCtx(): Ctx =
     result.fill = parseHtmlColor("black").rgbx
     result.stroke = parseHtmlColor("black").rgbx
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
   result.strokeWidth = 1
   result.transform = mat3()
   result.strokeMiterLimit = defaultMiterLimit
@@ -323,8 +321,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
   except PixieError as e:
     raise e
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
 
 proc fill(img: Image, ctx: Ctx, path: Path) {.inline.} =
   if ctx.display and ctx.opacity > 0:
@@ -560,8 +557,7 @@ proc draw(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) =
   except PixieError as e:
     raise e
   except:
-    let e = getCurrentException()
-    raise newException(PixieError, e.msg, e)
+    raise currentExceptionAsPixieError()
 
 proc decodeSvg*(
   data: string, width = 0, height = 0
diff --git a/src/pixie/fontformats/svgfont.nim b/src/pixie/fontformats/svgfont.nim
index 1370b05..9ad00e6 100644
--- a/src/pixie/fontformats/svgfont.nim
+++ b/src/pixie/fontformats/svgfont.nim
@@ -1,4 +1,5 @@
-import pixie/common, pixie/paths, strutils, tables, unicode, vmath, xmlparser, xmltree
+import pixie/common, pixie/internal, pixie/paths, strutils, tables, unicode,
+    vmath, xmlparser, xmltree
 
 type SvgFont* = ref object
   unitsPerEm*, ascent*, descent*: float32
@@ -42,8 +43,7 @@ proc parseSvgFont*(buf: string): SvgFont {.raises: [PixieError].} =
     try:
       parseXml(buf)
     except:
-      let e = getCurrentException()
-      raise newException(PixieError, e.msg, e)
+      raise currentExceptionAsPixieError()
 
   let defs = root.child("defs")
   if defs == nil:
diff --git a/src/pixie/internal.nim b/src/pixie/internal.nim
index 4a19cf1..4bf2582 100644
--- a/src/pixie/internal.nim
+++ b/src/pixie/internal.nim
@@ -3,6 +3,11 @@ import chroma, vmath
 when defined(amd64) and not defined(pixieNoSimd):
   import nimsimd/sse2
 
+template currentExceptionAsPixieError*(): untyped =
+  ## Gets the current exception and returns it as a PixieError with stack trace.
+  let e = getCurrentException()
+  newException(PixieError, e.getStackTrace & e.msg, e)
+
 proc gaussianKernel*(radius: int): seq[uint16] {.raises: [].} =
   ## Compute lookup table for 1d Gaussian kernel.
   ## Values are [0, 255] * 256.
diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim
index 6352457..8f5c554 100644
--- a/src/pixie/paths.nim
+++ b/src/pixie/paths.nim
@@ -675,7 +675,7 @@ proc commandsToShapes(
       pow(t, 3) * to
 
     var
-      t: float32 # Where we are at on the curve from [0, 1]
+      t: float32       # Where we are at on the curve from [0, 1]
       step = 1.float32 # How far we want to try to move along the curve
       prev = at
       next = compute(at, ctrl1, ctrl2, to, t + step)
@@ -706,7 +706,7 @@ proc commandsToShapes(
       pow(t, 2) * to
 
     var
-      t: float32 # Where we are at on the curve from [0, 1]
+      t: float32       # Where we are at on the curve from [0, 1]
       step = 1.float32 # How far we want to try to move along the curve
       prev = at
       next = compute(at, ctrl, to, t + step)
@@ -825,7 +825,7 @@ proc commandsToShapes(
     let arc = endpointToCenterArcParams(at, radii, rotation, large, sweep, to)
 
     var
-      t: float32 # Where we are at on the curve from [0, 1]
+      t: float32       # Where we are at on the curve from [0, 1]
       step = 1.float32 # How far we want to try to move along the curve
       prev = at
     while t != 1:
@@ -2330,7 +2330,8 @@ when defined(pixieSweeps):
       let
         sweepHeight = cutLines[currCutLine + 1] - cutLines[currCutLine]
         yFracTop = ((y.float32 - cutLines[currCutLine]) / sweepHeight).clamp(0, 1)
-        yFracBottom = ((y.float32 + 1 - cutLines[currCutLine]) / sweepHeight).clamp(0, 1)
+        yFracBottom = ((y.float32 + 1 - cutLines[currCutLine]) /
+            sweepHeight).clamp(0, 1)
       var i = 0
       while i < sweep.len:
         let
@@ -2352,11 +2353,13 @@ when defined(pixieSweeps):
           nw = vec2(sweep[i+0].atx, cutLines[currCutLine])
           sw = vec2(sweep[i+0].tox, cutLines[currCutLine + 1])
         for x in minWi ..< maxWi:
-          var area = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(x.float32, y.float32))
+          var area = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(
+              x.float32, y.float32))
           coverages[x - startX] += (area * 255).uint8
 
         let x = maxWi
-        var midArea = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(x.float32, y.float32))
+        var midArea = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(
+            x.float32, y.float32))
         var midArea8 = (midArea * 255).uint8
         for x in maxWi ..< minEi:
           # TODO: Maybe try coverages of uint16 to prevent streeks in solid white fill?
@@ -2366,7 +2369,8 @@ when defined(pixieSweeps):
           ne = vec2(sweep[i+1].atx, cutLines[currCutLine])
           se = vec2(sweep[i+1].tox, cutLines[currCutLine + 1])
         for x in minEi ..< maxEi:
-          var area = midArea - pixelCover(ne - vec2(x.float32, y.float32), se - vec2(x.float32, y.float32))
+          var area = midArea - pixelCover(ne - vec2(x.float32, y.float32), se -
+              vec2(x.float32, y.float32))
           coverages[x - startX] += (area * 255).uint8
 
         i += 2
@@ -2382,7 +2386,8 @@ when defined(pixieSweeps):
         inc currCutLine
         if currCutLine == sweeps.len:
           break
-        coverages.computeCoverage(scanLine, startX, cutLines, currCutLine, sweeps[currCutLine])
+        coverages.computeCoverage(scanLine, startX, cutLines, currCutLine,
+            sweeps[currCutLine])
 
       image.fillCoverage(
         rgbx,