Skip to content

Replace ConfigurationError with coded Panic system#22

Merged
adelrodriguez merged 1 commit intomainfrom
03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics
Mar 7, 2026
Merged

Replace ConfigurationError with coded Panic system#22
adelrodriguez merged 1 commit intomainfrom
03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics

Conversation

@adelrodriguez
Copy link
Copy Markdown
Owner

This pull request replaces the generic ConfigurationError with a more structured Panic error system that includes machine-readable error codes.

Key Changes

Error System Overhaul:

  • Removed ConfigurationError export and replaced with Panic class
  • Added PanicCode type with 20+ specific error codes (e.g., FLOW_NO_EXIT, TASK_SELF_REFERENCE, RUN_SYNC_TRY_PROMISE)
  • Panic now exposes a code property for programmatic error handling
  • Updated error messages to use predefined constants from PanicMessages

API Changes:

  • Renamed retryOptions export to createRetryPolicy
  • All programmer errors now throw Panic instead of returning error values
  • Error constructors simplified to use standard ErrorOptions pattern

Enhanced Error Handling:

  • Added invariant utility function for assertion-style error checking
  • Updated assertUnreachable to accept a panic code parameter
  • Improved error context with specific codes for different failure scenarios

Test Updates:

  • Added expectPanic helper function for consistent panic testing
  • Updated all error assertions to check for specific panic codes
  • Enhanced test coverage for invalid task handlers and configuration errors

The new system provides better debugging capabilities through structured error codes while maintaining backward compatibility for runtime error handling.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 6, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58974750-51a6-4ce4-9558-15706559e845

📥 Commits

Reviewing files that changed from the base of the PR and between 9713e41 and 4e6307b.

📒 Files selected for processing (26)
  • README.md
  • src/__tests__/index.test.ts
  • src/__tests__/types.test.ts
  • src/index.ts
  • src/lib/__tests__/utils.test.ts
  • src/lib/builder.ts
  • src/lib/errors.ts
  • src/lib/executors/__tests__/all.test.ts
  • src/lib/executors/__tests__/flow.test.ts
  • src/lib/executors/__tests__/retry.test.ts
  • src/lib/executors/__tests__/run-sync.test.ts
  • src/lib/executors/__tests__/run.test.ts
  • src/lib/executors/__tests__/shared.test.ts
  • src/lib/executors/all.ts
  • src/lib/executors/base.ts
  • src/lib/executors/flow.ts
  • src/lib/executors/gen.ts
  • src/lib/executors/run-sync.ts
  • src/lib/executors/run.ts
  • src/lib/executors/shared.ts
  • src/lib/modifiers/__tests__/retry.test.ts
  • src/lib/modifiers/retry.ts
  • src/lib/modifiers/signal.ts
  • src/lib/modifiers/timeout.ts
  • src/lib/types/builder.ts
  • src/lib/utils.ts

Walkthrough

This PR replaces ConfigurationError with a new Panic error model (class Panic, PanicCode, PanicMessages) and updates error constructors to use message+options. It adds invariant() and changes assertUnreachable to accept PanicCode. Many runtime checks and tests were converted to throw/expect Panic codes (e.g., WRAP_UNAVAILABLE, RUN_SYNC_TRY_PROMISE, FLOW_NO_EXIT, TASK_UNKNOWN_REFERENCE). The retry API was renamed: normalizeRetryPolicy/retryOptions → createRetryPolicy. Public exports updated accordingly (added Panic/PanicCode/createRetryPolicy; removed ConfigurationError/retryOptions).

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: replacing ConfigurationError with a coded Panic error system throughout the codebase.
Description check ✅ Passed The description clearly explains the error system overhaul, API changes, enhanced error handling, and test updates, all aligned with the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Owner Author

adelrodriguez commented Mar 6, 2026

@adelrodriguez
Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

264-311: ⚠️ Potential issue | 🟡 Minor

Remove returned ConfigErrors from the documented run signatures.

The new Panic notes are correct, but the Common signatures section still says run(...) returns ConfigErrors. That leaves the public contract internally inconsistent after the Panic migration.

📝 Suggested doc fix
-- `run(tryFn)` -> `Promise<T | UnhandledException | ConfigErrors>`
-- `run({ try, catch })` -> `Promise<T | C | ConfigErrors>`
+- `run(tryFn)` -> `Promise<T | UnhandledException>`
+- `run({ try, catch })` -> `Promise<T | C>`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 264 - 311, Update the "Common signatures" docs to
remove any reference to returned `ConfigErrors` from the `run` signatures:
change the listed signatures so `run(tryFn)` returns `Promise<T |
UnhandledException>` and `run({ try, catch })` returns `Promise<T | C |
UnhandledException>` (or equivalent generic `C` for catch results), leaving
`runSync`, `all`, `settled().all`, and `flow` unchanged; update any nearby lines
in the same "Common signatures" block that still mention `ConfigErrors` so the
public contract is consistent with the Panic migration.
🧹 Nitpick comments (1)
src/lib/executors/__tests__/retry.test.ts (1)

175-206: Use the shared expectPanic helper for these cases.

Both tests duplicate the same try/catch + Panic.code assertion flow. Switching them to the helper added in this PR would keep the panic assertions shorter and centralized.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/executors/__tests__/retry.test.ts` around lines 175 - 206, Replace
the duplicated try/catch panic assertions in the two tests that call
executeRunSync with the shared expectPanic helper: call expectPanic(() =>
executeRunSync(...), "RUN_SYNC_ASYNC_RETRY_POLICY") (or the helper's signature
used in the test suite) in the tests titled "throws Panic when sync runner is
used with async-required retry policy" and "throws Panic when sync runner is
used with jittered constant retry" so both assertions use expectPanic instead of
manual try/catch and explicit Panic.code checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/types.test.ts`:
- Around line 153-169: Add a type-regression test to cover the renamed
retry-policy export: in src/__tests__/types.test.ts add a new conditional
typecheck block (similar to the existing "panic" block) that references
createRetryPolicy to assert its exported type (e.g., declare a const rp =
try$.createRetryPolicy(...) and use Expect/Equal to pin the expected
return/parameter types) and include a line with // `@ts-expect-error` to assert
that try$.retryOptions no longer exists; use the unique symbols
createRetryPolicy and retryOptions so the test fails if the public API drifts.

In `@src/index.ts`:
- Around line 27-29: Add compile-time pins for the new root exports by updating
src/__tests__/types.test.ts to import createRetryPolicy and the PanicCode type
from the package root and assert their presence at type-check time;
specifically, import createRetryPolicy (value) and PanicCode (type) and create
minimal type-level assertions or dummy assignments that will fail TS compilation
if those exports are missing or change signature, ensuring the public API change
is covered by the test suite.

In `@src/lib/errors.ts`:
- Around line 50-76: The constructors for CancellationError, TimeoutError,
RetryExhaustedError, and UnhandledException must remain backward-compatible with
the legacy call shape where the first argument may be an options object (e.g.,
new UnhandledException({ cause })); update each constructor (e.g., in class
UnhandledException and RetryExhaustedError) to accept (messageOrOptions?: string
| ErrorOptions, options?: ErrorOptions), detect if the first arg is an object
and no message string was provided, then normalize to message and options (use
the default message when message is omitted) before calling super and setting
name; this preserves JS/TS compatibility without forcing consumers to change,
and if you instead keep the new strict signature remember to pin the API types
in src/__tests__/types.test.ts.

In `@src/lib/executors/all.ts`:
- Around line 48-64: The async branch currently awaits the catch handler promise
(mapped) directly which allows long-running catch handlers to ignore the
builder's timeout/abort and masks timeout/cancellation as
ALL_CATCH_HANDLER_REJECT; in the checkIsPromiseLike(mapped) branch wrap mapped
with the executor's existing timeout/cancellation mechanism (race mapped against
the builder's timeout and abort signal or use the helper that enforces the same
AbortSignal/timeout used for the rest of all(...)), so that a TimeoutError or
CancellationError is surfaced instead of ALL_CATCH_HANDLER_REJECT; preserve the
existing Panic("ALL_CATCH_HANDLER_THROW", ...) for synchronous throwers and only
convert mapped rejections that are real handler errors, while letting
timeout/cancellation propagate from the new wrapper (refer to catchFn, mapped,
checkIsPromiseLike, Panic, ALL_CATCH_HANDLER_REJECT).

In `@src/lib/executors/flow.ts`:
- Around line 203-208: The current code inspects the resolved value from
this.#executeAttempt() with instanceof to detect cancellation/timeout, which
misclassifies legitimate $exit(new TimeoutError()/CancellationError()) as
failures; instead, await this.#executeAttempt() inside a try/catch and only
rethrow TimeoutError or CancellationError if they were thrown (i.e., caught),
not when returned as a value. Remove the instanceof checks on the resolved value
in the shown block (and the similar checks around the other occurrence near the
242-244 region) so that returned TimeoutError/CancellationError values are
treated as normal successful results and only thrown exceptions are propagated
as aborts.

In `@src/lib/executors/gen.ts`:
- Around line 28-29: The catch blocks in gen currently wrap all throwables into
UnhandledException, which loses control-flow errors; update each catch (the ones
returning UnhandledException at the spots shown) to first detect control errors
(if error is instance of Panic, CancellationError, or TimeoutError) and
return/propagate that error as-is cast to GenErrors<TYield>, and only construct
new UnhandledException for unknown throwables; apply the same change to all
similar catch sites in gen.ts (the returns at the indicated locations).

In `@src/lib/executors/run-sync.ts`:
- Around line 214-219: The try/catch around invoking catchFn currently wraps any
thrown Panic into a new Panic with code RUN_SYNC_CATCH_HANDLER_THROW, which
hides the original RUN_SYNC_CATCH_PROMISE; update the catch block in the
run-sync path so that if the caught error (catchError) is an instance of Panic
with the code "RUN_SYNC_CATCH_PROMISE" it is rethrown unchanged, otherwise wrap
it into new Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError }); keep
references to catchFn, assertNotPromiseLike, mapped, Panic,
RUN_SYNC_CATCH_PROMISE and RUN_SYNC_CATCH_HANDLER_THROW to locate and implement
this conditional rethrow.

---

Outside diff comments:
In `@README.md`:
- Around line 264-311: Update the "Common signatures" docs to remove any
reference to returned `ConfigErrors` from the `run` signatures: change the
listed signatures so `run(tryFn)` returns `Promise<T | UnhandledException>` and
`run({ try, catch })` returns `Promise<T | C | UnhandledException>` (or
equivalent generic `C` for catch results), leaving `runSync`, `all`,
`settled().all`, and `flow` unchanged; update any nearby lines in the same
"Common signatures" block that still mention `ConfigErrors` so the public
contract is consistent with the Panic migration.

---

Nitpick comments:
In `@src/lib/executors/__tests__/retry.test.ts`:
- Around line 175-206: Replace the duplicated try/catch panic assertions in the
two tests that call executeRunSync with the shared expectPanic helper: call
expectPanic(() => executeRunSync(...), "RUN_SYNC_ASYNC_RETRY_POLICY") (or the
helper's signature used in the test suite) in the tests titled "throws Panic
when sync runner is used with async-required retry policy" and "throws Panic
when sync runner is used with jittered constant retry" so both assertions use
expectPanic instead of manual try/catch and explicit Panic.code checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 20537298-3590-45e3-9065-ef830290b8c3

📥 Commits

Reviewing files that changed from the base of the PR and between 93b7966 and 1ae39b1.

📒 Files selected for processing (26)
  • README.md
  • src/__tests__/index.test.ts
  • src/__tests__/types.test.ts
  • src/index.ts
  • src/lib/__tests__/utils.test.ts
  • src/lib/builder.ts
  • src/lib/errors.ts
  • src/lib/executors/__tests__/all.test.ts
  • src/lib/executors/__tests__/flow.test.ts
  • src/lib/executors/__tests__/retry.test.ts
  • src/lib/executors/__tests__/run-sync.test.ts
  • src/lib/executors/__tests__/run.test.ts
  • src/lib/executors/__tests__/shared.test.ts
  • src/lib/executors/all.ts
  • src/lib/executors/base.ts
  • src/lib/executors/flow.ts
  • src/lib/executors/gen.ts
  • src/lib/executors/run-sync.ts
  • src/lib/executors/run.ts
  • src/lib/executors/shared.ts
  • src/lib/modifiers/__tests__/retry.test.ts
  • src/lib/modifiers/retry.ts
  • src/lib/modifiers/signal.ts
  • src/lib/modifiers/timeout.ts
  • src/lib/types/builder.ts
  • src/lib/utils.ts
💤 Files with no reviewable changes (1)
  • src/lib/types/builder.ts

@adelrodriguez adelrodriguez force-pushed the 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics branch from 1ae39b1 to 9713e41 Compare March 7, 2026 00:19
@adelrodriguez
Copy link
Copy Markdown
Owner Author

@codex review

@adelrodriguez
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/lib/executors/run-sync.ts (1)

214-219: ⚠️ Potential issue | 🟠 Major

RUN_SYNC_CATCH_PROMISE panic is incorrectly wrapped as RUN_SYNC_CATCH_HANDLER_THROW.

The assertNotPromiseLike call at line 216 is inside the try block, so if catchFn returns a Promise, the resulting Panic("RUN_SYNC_CATCH_PROMISE") is caught and rewrapped as RUN_SYNC_CATCH_HANDLER_THROW, losing the more specific error code.

🛠️ Suggested fix
     try {
       const mapped = catchFn(error)
-      assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE")
-      return mapped
     } catch (catchError) {
+      if (catchError instanceof Panic) {
+        throw catchError
+      }
       throw new Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError })
     }
+
+    assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE")
+    return mapped
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/executors/run-sync.ts` around lines 214 - 219, The try-block
currently encloses both calling catchFn and assertNotPromiseLike so a
RUN_SYNC_CATCH_PROMISE Panic (thrown by assertNotPromiseLike) is caught and
rewrapped as RUN_SYNC_CATCH_HANDLER_THROW; fix by limiting the try to only the
call to catchFn: call const mapped = catchFn(error) inside try and catch only
that throw (rethrow as Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError
})), then call assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE") outside
the try so a promise-like detection raises the correct Panic and is not
rewrapped.
🧹 Nitpick comments (1)
src/lib/modifiers/timeout.ts (1)

71-75: Consider extracting the repeated TimeoutError construction pattern.

The same conditional pattern for creating TimeoutError with or without a cause appears in three places (#abort, checkDidTimeout, and race). While the current approach works, extracting a small private helper could reduce duplication.

♻️ Optional refactor
+  `#createTimeoutError`(cause?: unknown): TimeoutError {
+    return cause === undefined
+      ? new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`)
+      : new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`, { cause })
+  }
+
   `#abort`(cause?: unknown): TimeoutError {
-    const timeoutError =
-      cause === undefined
-        ? new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`)
-        : new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`, { cause })
+    const timeoutError = this.#createTimeoutError(cause)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/modifiers/timeout.ts` around lines 71 - 75, Extract the repeated
TimeoutError construction into a private helper (e.g., a method like
createTimeoutError(cause?: unknown)) and replace the three duplicated
conditional constructions in `#abort`, checkDidTimeout, and race to call this
helper; the helper should return new TimeoutError(`Execution exceeded timeout of
${this.#timeoutMs}ms`) when no cause is provided or new TimeoutError(..., {
cause }) when a cause/reason is passed, keeping existing behavior and message
formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/lib/executors/run-sync.ts`:
- Around line 214-219: The try-block currently encloses both calling catchFn and
assertNotPromiseLike so a RUN_SYNC_CATCH_PROMISE Panic (thrown by
assertNotPromiseLike) is caught and rewrapped as RUN_SYNC_CATCH_HANDLER_THROW;
fix by limiting the try to only the call to catchFn: call const mapped =
catchFn(error) inside try and catch only that throw (rethrow as
Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError })), then call
assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE") outside the try so a
promise-like detection raises the correct Panic and is not rewrapped.

---

Nitpick comments:
In `@src/lib/modifiers/timeout.ts`:
- Around line 71-75: Extract the repeated TimeoutError construction into a
private helper (e.g., a method like createTimeoutError(cause?: unknown)) and
replace the three duplicated conditional constructions in `#abort`,
checkDidTimeout, and race to call this helper; the helper should return new
TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`) when no cause
is provided or new TimeoutError(..., { cause }) when a cause/reason is passed,
keeping existing behavior and message formatting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8148f013-9e37-468e-a1ec-486f918682fa

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae39b1 and 9713e41.

📒 Files selected for processing (26)
  • README.md
  • src/__tests__/index.test.ts
  • src/__tests__/types.test.ts
  • src/index.ts
  • src/lib/__tests__/utils.test.ts
  • src/lib/builder.ts
  • src/lib/errors.ts
  • src/lib/executors/__tests__/all.test.ts
  • src/lib/executors/__tests__/flow.test.ts
  • src/lib/executors/__tests__/retry.test.ts
  • src/lib/executors/__tests__/run-sync.test.ts
  • src/lib/executors/__tests__/run.test.ts
  • src/lib/executors/__tests__/shared.test.ts
  • src/lib/executors/all.ts
  • src/lib/executors/base.ts
  • src/lib/executors/flow.ts
  • src/lib/executors/gen.ts
  • src/lib/executors/run-sync.ts
  • src/lib/executors/run.ts
  • src/lib/executors/shared.ts
  • src/lib/modifiers/__tests__/retry.test.ts
  • src/lib/modifiers/retry.ts
  • src/lib/modifiers/signal.ts
  • src/lib/modifiers/timeout.ts
  • src/lib/types/builder.ts
  • src/lib/utils.ts
💤 Files with no reviewable changes (1)
  • src/lib/types/builder.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/executors/tests/run.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/lib/executors/tests/shared.test.ts
  • src/lib/modifiers/signal.ts
  • src/lib/executors/gen.ts
  • src/tests/types.test.ts
  • src/lib/executors/tests/all.test.ts
  • src/lib/utils.ts
  • src/lib/executors/shared.ts

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/lib/executors/run-sync.ts (1)

214-219: ⚠️ Potential issue | 🟠 Major

Preserve RUN_SYNC_CATCH_PROMISE instead of rewrapping it.

The assertNotPromiseLike call at line 216 can throw Panic("RUN_SYNC_CATCH_PROMISE"), but it's inside the same try block that catches and rewraps all errors as RUN_SYNC_CATCH_HANDLER_THROW. This causes the more specific panic code to be lost.

💡 Suggested fix
     try {
       const mapped = catchFn(error)
-      assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE")
-      return mapped
     } catch (catchError) {
+      if (catchError instanceof Panic) {
+        throw catchError
+      }
       throw new Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError })
     }
+
+    assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE")
+    return mapped
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/executors/run-sync.ts` around lines 214 - 219, The current try/catch
wraps both the call to catchFn and the assertNotPromiseLike check so any Panic
thrown by assertNotPromiseLike (RUN_SYNC_CATCH_PROMISE) gets rewrapped as
RUN_SYNC_CATCH_HANDLER_THROW; fix it by narrowing the try to only invoke
catchFn: call const mapped = catchFn(error) inside the try and let the catch
block rewrap errors from catchFn only, then call assertNotPromiseLike(mapped,
"RUN_SYNC_CATCH_PROMISE") outside that try (so its Panic escapes intact) and
finally return mapped; reference functions/symbols: catchFn,
assertNotPromiseLike, Panic, RUN_SYNC_CATCH_PROMISE,
RUN_SYNC_CATCH_HANDLER_THROW.
🧹 Nitpick comments (1)
src/lib/modifiers/timeout.ts (1)

41-44: Consider extracting a local helper to reduce duplication.

The same conditional TimeoutError construction pattern appears three times. A small private helper could improve maintainability.

♻️ Optional refactor to reduce duplication
+  `#createTimeoutError`(cause?: unknown): TimeoutError {
+    const message = `Execution exceeded timeout of ${this.#timeoutMs}ms`
+    return cause === undefined
+      ? new TimeoutError(message)
+      : new TimeoutError(message, { cause })
+  }
+
   `#abort`(cause?: unknown): TimeoutError {
-    const timeoutError =
-      cause === undefined
-        ? new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`)
-        : new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`, { cause })
+    const timeoutError = this.#createTimeoutError(cause)

Then use this.#createTimeoutError(cause) in checkDidTimeout and race as well.

Also applies to: 71-75, 107-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/modifiers/timeout.ts` around lines 41 - 44, Extract a private helper
that centralizes TimeoutError construction to eliminate the repeated conditional
pattern: create a method (e.g., `#createTimeoutError`(cause?: Error)) that returns
new TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`) or new
TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`, { cause })
depending on whether cause is provided, then replace the three duplicated
constructions in checkDidTimeout, race, and the other location with calls to
this.#createTimeoutError(cause).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/lib/executors/run-sync.ts`:
- Around line 214-219: The current try/catch wraps both the call to catchFn and
the assertNotPromiseLike check so any Panic thrown by assertNotPromiseLike
(RUN_SYNC_CATCH_PROMISE) gets rewrapped as RUN_SYNC_CATCH_HANDLER_THROW; fix it
by narrowing the try to only invoke catchFn: call const mapped = catchFn(error)
inside the try and let the catch block rewrap errors from catchFn only, then
call assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE") outside that try (so
its Panic escapes intact) and finally return mapped; reference
functions/symbols: catchFn, assertNotPromiseLike, Panic, RUN_SYNC_CATCH_PROMISE,
RUN_SYNC_CATCH_HANDLER_THROW.

---

Nitpick comments:
In `@src/lib/modifiers/timeout.ts`:
- Around line 41-44: Extract a private helper that centralizes TimeoutError
construction to eliminate the repeated conditional pattern: create a method
(e.g., `#createTimeoutError`(cause?: Error)) that returns new
TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`) or new
TimeoutError(`Execution exceeded timeout of ${this.#timeoutMs}ms`, { cause })
depending on whether cause is provided, then replace the three duplicated
constructions in checkDidTimeout, race, and the other location with calls to
this.#createTimeoutError(cause).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c5c0691d-0ac6-4ba6-9209-8b6a3774e7b1

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae39b1 and 9713e41.

📒 Files selected for processing (26)
  • README.md
  • src/__tests__/index.test.ts
  • src/__tests__/types.test.ts
  • src/index.ts
  • src/lib/__tests__/utils.test.ts
  • src/lib/builder.ts
  • src/lib/errors.ts
  • src/lib/executors/__tests__/all.test.ts
  • src/lib/executors/__tests__/flow.test.ts
  • src/lib/executors/__tests__/retry.test.ts
  • src/lib/executors/__tests__/run-sync.test.ts
  • src/lib/executors/__tests__/run.test.ts
  • src/lib/executors/__tests__/shared.test.ts
  • src/lib/executors/all.ts
  • src/lib/executors/base.ts
  • src/lib/executors/flow.ts
  • src/lib/executors/gen.ts
  • src/lib/executors/run-sync.ts
  • src/lib/executors/run.ts
  • src/lib/executors/shared.ts
  • src/lib/modifiers/__tests__/retry.test.ts
  • src/lib/modifiers/retry.ts
  • src/lib/modifiers/signal.ts
  • src/lib/modifiers/timeout.ts
  • src/lib/types/builder.ts
  • src/lib/utils.ts
💤 Files with no reviewable changes (1)
  • src/lib/types/builder.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/lib/executors/tests/run.test.ts
  • src/lib/tests/utils.test.ts
  • README.md
  • src/lib/executors/all.ts
  • src/lib/modifiers/tests/retry.test.ts
  • src/index.ts
  • src/lib/executors/tests/shared.test.ts
  • src/lib/executors/tests/flow.test.ts
  • src/lib/executors/run.ts

@adelrodriguez adelrodriguez force-pushed the 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics branch from 9713e41 to 86502b7 Compare March 7, 2026 00:36
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86502b7dde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +216 to +219
assertNotPromiseLike(mapped, "RUN_SYNC_CATCH_PROMISE")
return mapped
} catch (catchError) {
throw new Panic({ cause: catchError })
throw new Panic("RUN_SYNC_CATCH_HANDLER_THROW", { cause: catchError })
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve RUN_SYNC_CATCH_PROMISE code in runSync catch path

When runSync({ try, catch }) handles an error and the provided catch returns a Promise (via unsafe cast), assertNotPromiseLike throws Panic("RUN_SYNC_CATCH_PROMISE"), but that panic is immediately caught by the surrounding try/catch and rethrown as RUN_SYNC_CATCH_HANDLER_THROW. This misclassifies a non-throwing catch handler as a throw, so consumers relying on Panic.code get incorrect diagnostics for this input case.

Useful? React with 👍 / 👎.

@adelrodriguez adelrodriguez force-pushed the 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics branch 2 times, most recently from 5f0170c to ae1c392 Compare March 7, 2026 00:49
@adelrodriguez adelrodriguez force-pushed the 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics branch from ae1c392 to 4e6307b Compare March 7, 2026 00:50
@adelrodriguez adelrodriguez merged commit 39e39a0 into main Mar 7, 2026
6 of 7 checks passed
@adelrodriguez adelrodriguez deleted the 03-06-replace_configurationerror_with_coded_panic_for_better_diagnostics branch March 7, 2026 00:51
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e6307b2b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +60 to +61
if (raced instanceof CancellationError || raced instanceof TimeoutError) {
throw raced
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return mapped Timeout/Cancellation values from all() catch

In executeAll's async catch path, this branch treats any CancellationError/TimeoutError result as a control failure, but raced can also be the user-provided mapped value from options.catch. That means a handler like catch: async () => new TimeoutError("fallback") now rejects instead of resolving with the mapped value, which is a behavioral regression introduced by this change. To preserve valid catch mappings, control precedence should be based on controller state (e.g., post-race control checks), not on the mapped value's runtime class.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant