<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Errors on PHP Boy Scout</title><link>https://blog-570662.gitlab.io/tags/errors/</link><description>Recent content in Errors on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Tue, 30 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog-570662.gitlab.io/tags/errors/index.xml" rel="self" type="application/rss+xml"/><item><title>A stack trace is not an error message</title><link>https://blog-570662.gitlab.io/a-stack-trace-is-not-an-error-message/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://blog-570662.gitlab.io/a-stack-trace-is-not-an-error-message/</guid><description>&lt;img src="https://blog-570662.gitlab.io/a-stack-trace-is-not-an-error-message/cover-a-stack-trace-is-not-an-error-message.png" alt="Featured image of post A stack trace is not an error message" /&gt;&lt;p&gt;The repair agent I&amp;rsquo;ve been building into go-tool-base narrates what it&amp;rsquo;s doing as it goes. It builds, it tests, it lints, it fixes, and it logs each step so I can watch it think. Mostly that log is a calm, readable trickle: tried this, that failed, reading the file, here&amp;rsquo;s the fix. Mostly.&lt;/p&gt;
&lt;p&gt;The moment a build or a lint step failed, the calm trickle turned into a wall of Go stack frames, the same forty lines of runtime gubbins over and over, burying the one line I actually wanted to read.&lt;/p&gt;
&lt;h2 id="two-different-things-we-both-call-the-error"&gt;Two different things we both call &amp;ldquo;the error&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;The agent&amp;rsquo;s tools wrap their failures with &lt;a class="link" href="https://github.com/cockroachdb/errors" target="_blank" rel="noopener"
 &gt;cockroachdb/errors&lt;/a&gt;, which is a lovely library: it attaches a stack trace to an error the moment you create it, so when something goes wrong deep in the weeds you can see exactly how you got there. A failed &lt;code&gt;go build&lt;/code&gt; comes back as one of these rich, wrapped values, carrying its message &lt;em&gt;and&lt;/em&gt; its stack.&lt;/p&gt;
&lt;p&gt;The line that recorded the failure looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tool execution failed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looks fine. It is not fine. The logger is &lt;a class="link" href="https://github.com/charmbracelet/log" target="_blank" rel="noopener"
 &gt;charmbracelet/log&lt;/a&gt;, and when you hand a structured logger a cockroachdb error &lt;em&gt;value&lt;/em&gt; as a field, it renders the whole thing: message, wraps, types, and every frame of that attached stack. So every failed step, and during self-repair there are plenty, printed a full traceback at WARN. The signal, the actual build error, was in there somewhere, wearing a forty-line coat.&lt;/p&gt;
&lt;p&gt;The thing is, a stack trace and an error message are two different objects that we lazily both call &amp;ldquo;the error&amp;rdquo;. &lt;code&gt;err.Error()&lt;/code&gt; is the message: short, human, &amp;ldquo;lint issues found&amp;hellip;&amp;rdquo;. The value &lt;code&gt;err&lt;/code&gt; is the message &lt;em&gt;plus&lt;/em&gt; the evidence of where it came from. They serve different readers. The message is for whoever&amp;rsquo;s watching the loop run. The stack is for whoever&amp;rsquo;s debugging why the loop itself is broken. Hand the wrong one to the wrong reader and you&amp;rsquo;ve got either noise or a mystery.&lt;/p&gt;
&lt;h2 id="pick-a-reader-per-level"&gt;Pick a reader per level
&lt;/h2&gt;&lt;p&gt;The &lt;a class="link" href="https://gitlab.com/phpboyscout/go-tool-base/-/blob/570964c/pkg/chat/tools.go#L91-L99" target="_blank" rel="noopener"
 &gt;fix&lt;/a&gt; is to stop making one log line serve both:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tool execution failed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tool execution failure detail&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The message goes to WARN, where someone&amp;rsquo;s watching the agent work and just wants to know what failed. The full wrapped value, stack and all, goes to DEBUG, where someone&amp;rsquo;s gone looking for trouble and wants every frame. Turn the level up and the evidence is right there; leave it at the default and the loop reads like prose again.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a one-line change, give or take, and it lives in the shared chat tool-dispatch path rather than in the agent, so every tool-using client gets the quieter log for free. It&amp;rsquo;s the same loop I&amp;rsquo;d just &lt;a class="link" href="https://blog-570662.gitlab.io/the-agent-said-success-the-linter-disagreed/" &gt;taught to respect the linter&lt;/a&gt;; apparently I was determined to make it both honest &lt;em&gt;and&lt;/em&gt; readable in the same fortnight.&lt;/p&gt;
&lt;h2 id="where-the-stack-belongs"&gt;Where the stack belongs
&lt;/h2&gt;&lt;p&gt;The stack trace was never the thing I needed to hide. cockroachdb/errors attaching it is exactly what I want; it&amp;rsquo;s the whole reason I use the library. The mistake was &lt;em&gt;where I let it surface&lt;/em&gt;. A trace dumped at WARN on every routine failure isn&amp;rsquo;t observability, it&amp;rsquo;s wallpaper, and wallpaper is what you stop seeing. Keep the loud version for the level where someone&amp;rsquo;s actually gone looking for it, and leave the everyday log alone. The stack was never the noise. Printing it on every line was.&lt;/p&gt;</description></item></channel></rss>