Handle thrown values that are not Errors - #185
Open
DmitrySharabin wants to merge 1 commit into
Open
DmitrySharabin wants to merge 1 commit into
DmitrySharabin wants to merge 1 commit into
Conversation
✅ Deploy Preview for h-test ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
DmitrySharabin
force-pushed
the
fix/183-non-error-throws
branch
3 times, most recently
from
September 11, 2026 13:22
14d7cfb to
cdf50ce
Compare
Throwing a primitive crashed the runner and took down the whole run: five sites assumed the caught value was an object. Normalize it once at each catch instead, keeping the original as cause. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RaXmFiBg3UGUMuGFXjn5Ki
DmitrySharabin
force-pushed
the
fix/183-non-error-throws
branch
from
September 11, 2026 13:40
cdf50ce to
e6b02b5
Compare
LeaVerou
requested changes
Sep 12, 2026
LeaVerou
left a comment
Member
There was a problem hiding this comment.
Can we add a property to the Error object that we are creating (or just make it our own subclass) so that we know to unwrap it instead of falsely reporting that an Error was thrown when it wasn't?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #183.
Throwing a non-
Erroris legal JS, and real code does it. hTest crashed on it — and took the whole run down, not just the offending test. What the issue reported as one crash is five symptoms of one assumption: that the caught value is an object.if ("actual" in e)inthrows on a primitive receivere.source = "beforeAll"(5 hook catches)pass: !!this.errorthrows: falsepassed for a test that threwnull!("actual" in this.error)throwscriterion is set${this.error.stack}throw null; phantomundefinedline otherwise${e.message}in themap()/check()catchesmap() failed. undefined#3 is the one to worry about: a silent false negative, not a crash.
The change
Normalize where the value enters, rather than teaching five downstream sites to cope with primitives:
plus one
e = asError(e);in each catch block. Every existing line in those blocks is untouched: with the value normalized up front,e.source = …,${e.message},"actual" in eand${this.error.stack}all work as written.Nothing else moves —
"actual" in e,!!this.errorand${this.error.stack}stay exactly as they were, because with an object they were never wrong.The original value survives as
cause, so a predicate can still match it:Verification
126/126. Each new test was confirmed red against the unfixed code, and the set was reduced by mutation testing: two candidates that never failed alone across six mutations (including a "guard theinsites instead of wrapping" alternative implementation) were dropped as subsumed.throw "boom" | 42 | null | undefined,throws: truethrow null,throws: falsethrow "boom", nothrowsGot error Error: boombeforeAll/beforeEach/afterEachthrows a stringbeforeEach: boomthrows: TypeErrorwith a real Errorcheck()that throws an ErrorGot error Error: check() failedmap()/check()that throws a stringmap() failed. undefinedmap() failed. mapboomDiff stats
Substantive lines only (blank- and comment-only changes excluded).
src/util.jssrc/classes/TestResult.jstests/errors.jsSKILL.mdNotes
Got error Error: boomrather thanGot error boom, andthrows: e => e === "boom"becomese.cause === "boom". Both are new ground — the old behavior was a crash.asErrorlives insrc/util.jsby the file's own precedent: it holds single-consumer helpers (interceptConsoleis used only byTestResult.js,regexEscapeonly bymap.js), and the criterion is whether a helper knows anything about its caller. This one doesn't.SKILL.md'sthrowsrow now records that a non-Error is wrapped and the original is on.cause, per the project's SKILL.md-review rule forTestResult.js.Errorthrown from a hook still crashes one.source = …. Unchanged frommain, not part of Throwing a non-Error crashes the runner and takes down the whole run #183.🤖 Generated with Claude Code
https://claude.ai/code/session_01RaXmFiBg3UGUMuGFXjn5Ki