Findings No. 62
In Effect 4, an exception thrown outside `Effect.try` is a defect, and `Effect.catch` never sees it
Code that throws outside Effect.try becomes a defect that catch and catchTag skip, so a fail-open translation pipeline could reject. catchDefect closes it.
While moving CCRTA's translation pipeline onto Effect 4.1.2, code review caught a regression before the change merged. The old code wrapped each AI Gateway request in try/catch and failed open: if anything went wrong, the affected strings were served in English and the page still rendered. The Effect version modelled the expected failures as tagged errors (timeout, gateway-wide failure, rejection, malformed output) and handled them with catchTag and Effect.catch.
Some work still ran outside the Effect.try wrappers, such as building the dictionary from the parsed response and sanitizing each translation. In Effect, an exception thrown there is a defect. Defects travel in their own channel of the cause, and Effect.catch and catchTag only handle the typed error channel. An unexpected throw would therefore skip every handler, and translateBatch could reject instead of falling back to English.
The fix has two parts. The dictionary building moved inside the parsing Effect.try, so a throw there becomes a typed Malformed failure. Then Effect.catchDefect at the end of the chunk pipeline turns any remaining defect into Malformed as well, which sends it through the same bisection and reporting path as malformed model output. A test stubs a response whose ok getter throws a non-Error value whenever the batch contains one particular string. It checks that bisection isolates that string, that the other strings still translate, that the thrown value is reported once, and that translateBatch resolves.
When porting fail-open code from try/catch to Effect, find every place that can throw and wrap it in Effect.try. Add Effect.catchDefect as a backstop, because typed error handlers do not catch defects.
Receipt: CCRTA Website commit eae0f6c (src/lib/server/translation/translator.ts, src/lib/server/translation/translator.effect.test.ts; "fail-open on unexpected throws (defects)").