Skip to content

chore(cli): rename FernCliError to TaskAbortSignal#14746

Merged
FedeZara merged 1 commit intomainfrom
FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal
Apr 10, 2026
Merged

chore(cli): rename FernCliError to TaskAbortSignal#14746
FedeZara merged 1 commit intomainfrom
FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal

Conversation

@FedeZara
Copy link
Copy Markdown
Contributor

@FedeZara FedeZara commented Apr 8, 2026

Description

Part of the CLI error classification effort — see #14749 for full context.

Renames FernCliError to TaskAbortSignal to better reflect its purpose: it is a control-flow signal used to abort a task after an error has already been logged, not an error class itself. This clears the naming space for the CliError class introduced in #14749.

Changes Made

  • Renamed FernCliError class to TaskAbortSignal across all usages
  • Deleted packages/cli/task-context/src/FernCliError.ts, created packages/cli/task-context/src/TaskAbortSignal.ts
  • Updated all import { FernCliError }import { TaskAbortSignal } and instanceof FernCliErrorinstanceof TaskAbortSignal across CLI v1, CLI v2, seed, and test helpers

Context

The old FernCliError was thrown by failAndThrow() purely as a sentinel to unwind the call stack — it carried no error metadata. Renaming it to TaskAbortSignal makes this intent explicit and avoids confusion with the new CliError class that carries typed error codes for Sentry routing.

Testing

  • Existing tests pass (rename is mechanical)

@FedeZara FedeZara self-assigned this Apr 8, 2026
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

@FedeZara FedeZara changed the title refactor(cli): rename FernCliError to TaskAbortSignal chore(cli): rename FernCliError to TaskAbortSignal Apr 8, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-08T04:49:09Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 132s 171s 130s -2s (-1.5%)
go-sdk square 280s 345s 295s +15s (+5.4%)
java-sdk square 311s 371s 321s +10s (+3.2%)
php-sdk square 127s 155s 126s -1s (-0.8%)
python-sdk square 168s 204s 163s -5s (-3.0%)
ruby-sdk-v2 square 152s 192s 154s +2s (+1.3%)
rust-sdk square 137s 127s 132s -5s (-3.6%)
swift-sdk square 141s 486s 145s +4s (+2.8%)
ts-sdk square 142s 173s 140s -2s (-1.4%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-08T04:49:09Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from 2f457fe to b7324ed Compare April 9, 2026 12:22
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 92s -3s (-3.2%)
go-sdk square 107s 134s 105s -2s (-1.9%)
java-sdk square 290s 346s 295s +5s (+1.7%)
php-sdk square 85s 119s 86s +1s (+1.2%)
python-sdk square 130s 166s 130s +0s (+0.0%)
ruby-sdk-v2 square 115s 153s 112s -3s (-2.6%)
rust-sdk square 93s 95s 91s -2s (-2.2%)
swift-sdk square 104s 448s 102s -2s (-1.9%)
ts-sdk square 98s 134s 102s +4s (+4.1%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from b7324ed to b06c68c Compare April 9, 2026 13:55
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 89s -6s (-6.3%)
go-sdk square 107s 134s 104s -3s (-2.8%)
java-sdk square 290s 346s 287s -3s (-1.0%)
php-sdk square 85s 119s 89s +4s (+4.7%)
python-sdk square 130s 166s 124s -6s (-4.6%)
ruby-sdk-v2 square 115s 153s 115s +0s (+0.0%)
rust-sdk square 93s 95s 93s +0s (+0.0%)
swift-sdk square 104s 448s 101s -3s (-2.9%)
ts-sdk square 98s 134s 101s +3s (+3.1%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from b06c68c to 5842013 Compare April 9, 2026 14:54
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 92s -3s (-3.2%)
go-sdk square 107s 134s 105s -2s (-1.9%)
java-sdk square 290s 346s 285s -5s (-1.7%)
php-sdk square 85s 119s 84s -1s (-1.2%)
python-sdk square 130s 166s 127s -3s (-2.3%)
ruby-sdk-v2 square 115s 153s 114s -1s (-0.9%)
rust-sdk square 93s 95s 104s +11s (+11.8%)
swift-sdk square 104s 448s 106s +2s (+1.9%)
ts-sdk square 98s 134s 99s +1s (+1.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from 5842013 to 06590df Compare April 9, 2026 15:50
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 92s -3s (-3.2%)
go-sdk square 107s 134s 104s -3s (-2.8%)
java-sdk square 290s 346s 289s -1s (-0.3%)
php-sdk square 85s 119s 89s +4s (+4.7%)
python-sdk square 130s 166s 124s -6s (-4.6%)
ruby-sdk-v2 square 115s 153s 116s +1s (+0.9%)
rust-sdk square 93s 95s 91s -2s (-2.2%)
swift-sdk square 104s 448s 100s -4s (-3.8%)
ts-sdk square 98s 134s 85s -13s (-13.3%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from 06590df to 8c1a873 Compare April 9, 2026 22:38
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 93s -2s (-2.1%)
go-sdk square 107s 134s 101s -6s (-5.6%)
java-sdk square 290s 346s 157s -133s (-45.9%)
php-sdk square 85s 119s 88s +3s (+3.5%)
python-sdk square 130s 166s 125s -5s (-3.8%)
ruby-sdk-v2 square 115s 153s 116s +1s (+0.9%)
rust-sdk square 93s 95s 92s -1s (-1.1%)
swift-sdk square 104s 448s 99s -5s (-4.8%)
ts-sdk square 98s 134s 98s +0s (+0.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch 3 times, most recently from 48cd7ac to ddfbe75 Compare April 10, 2026 16:19
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 91s -9s (-9.0%)
go-sdk square 104s 137s 106s +2s (+1.9%)
java-sdk square 157s 191s 159s +2s (+1.3%)
php-sdk square 86s 122s 85s -1s (-1.2%)
python-sdk square 124s 167s 126s +2s (+1.6%)
ruby-sdk-v2 square 120s 152s 112s -8s (-6.7%)
rust-sdk square 92s 94s 95s +3s (+3.3%)
swift-sdk square 105s 476s 104s -1s (-1.0%)
ts-sdk square 102s 129s 106s +4s (+3.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from ddfbe75 to 5e8f14e Compare April 10, 2026 17:09
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 87s -13s (-13.0%)
go-sdk square 104s 137s 104s +0s (+0.0%)
java-sdk square 157s 191s 165s +8s (+5.1%)
php-sdk square 86s 122s 84s -2s (-2.3%)
python-sdk square 124s 167s 126s +2s (+1.6%)
ruby-sdk-v2 square 120s 152s 121s +1s (+0.8%)
rust-sdk square 92s 94s 91s -1s (-1.1%)
swift-sdk square 105s 476s 110s +5s (+4.8%)
ts-sdk square 102s 129s 97s -5s (-4.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara force-pushed the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch from 5e8f14e to 32b33b9 Compare April 10, 2026 18:33
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 93s -7s (-7.0%)
go-sdk square 104s 137s 103s -1s (-1.0%)
java-sdk square 157s 191s 149s -8s (-5.1%)
php-sdk square 86s 122s 87s +1s (+1.2%)
python-sdk square 124s 167s 127s +3s (+2.4%)
ruby-sdk-v2 square 120s 152s 116s -4s (-3.3%)
rust-sdk square 92s 94s 93s +1s (+1.1%)
swift-sdk square 105s 476s 84s -21s (-20.0%)
ts-sdk square 102s 129s 98s -4s (-3.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@FedeZara FedeZara merged commit 952cbd2 into main Apr 10, 2026
265 checks passed
@FedeZara FedeZara deleted the FedeZara/refactor/rename-fern-cli-error-to-task-abort-signal branch April 10, 2026 19:53
FedeZara added a commit that referenced this pull request Apr 14, 2026
…entry routing (#14749)

## Description

Introduces a structured `CliError` class in `@fern-api/task-context` with typed error codes and automatic Sentry routing. This replaces the ad-hoc `new Error(...)` pattern throughout the CLI with a system that categorizes errors into user-facing codes (e.g. `CONFIG_ERROR`, `AUTH_ERROR`) vs internal errors that should be reported to Sentry. It also simplifies error handling by removing redundant error classes and unifying the ones we already have.

> **Recommended review approach:** commit-by-commit. Each commit is self-contained and builds on the previous one.

### Prerequisite PRs

- #14746 — rename `FernCliError` to `TaskAbortSignal` (clears the naming space)
- #14747 — make CLI v1 telemetry synchronous (so `failWithoutThrowing` can report errors)
- #14748 — make CLI v2 telemetry synchronous (same, for CLI v2)

### Follow-up PRs

- #14750 — temporarily disable Sentry for unclassified errors (safety net while migrating)
- #14753 — migrate `@fern-api/cli` package (example of a package migration PR)
- ~34 more package migration PRs (one per package, converting `new Error(...)` → `new CliError(...)` and adding error codes to `failAndThrow` and `failWithoutThrowing` calls)
- #14752 — re-enable Sentry for unclassified errors (merge last, after all migrations)

## Design Decisions

### Two ways to trigger and track errors

There are two paths through which errors are captured and reported:

1. **Throwing `CliError` directly.** Code anywhere in the CLI can `throw new CliError({ message, code })`. The top-level catch handler in each CLI entry point (CLI v1's `runCli` catch in `cli.ts`, CLI v2's `withContext` catch in `withContext.ts`) intercepts it, resolves the error code, and routes it to Sentry and/or PostHog.

2. **Calling `failAndThrow` / `failWithoutThrowing` on the task context.** These methods log the error, resolve the code (explicit override > `CliError.code` > fallback), and report to Sentry if the code is Sentry-reportable. `failAndThrow` then throws a `TaskAbortSignal` to unwind the stack; `failWithoutThrowing` marks the task as failed and returns. This is the preferred path when the caller needs control over what happens after the error — for example, to continue processing other tasks or to clean up resources before aborting. It also adapts naturally to the existing error-handling patterns already used throughout the codebase.

Both paths converge on the same code-resolution logic (`resolveErrorCode`) and Sentry-routing rules (`shouldReportToSentry`), so tracking is consistent regardless of which path is used.

### Default behavior: unclassified errors are internal

Any error that doesn't carry a `CliError.Code` (i.e. a plain `new Error(...)`) is treated as `INTERNAL_ERROR` at the top-level catch boundaries — and therefore reported to Sentry. The assumption is that if nobody explicitly categorized an error as user-facing, it's likely an internal bug.

**Temporary exception:** the follow-up PR #14750 temporarily downgrades this so unclassified errors skip Sentry during the migration period, to avoid noise from the ~34 packages that haven't been migrated yet. Once all migrations land, #14752 re-enables it.

### Error code taxonomy

Errors are classified into 12 typed codes:

| Code | Sentry? | Description |
|------|---------|-------------|
| `INTERNAL_ERROR` | Yes | Unexpected bugs — should be investigated |
| `RESOLUTION_ERROR` | Yes | Type/reference resolution failures (likely IR bugs) |
| `IR_CONVERSION_ERROR` | Yes | IR generation failures |
| `CONTAINER_ERROR` | Yes | Docker container failures |
| `VERSION_ERROR` | Yes | Version parsing/compatibility issues |
| `PARSE_ERROR` | No | Malformed user input (YAML, OpenAPI, etc.) |
| `ENVIRONMENT_ERROR` | No | Missing env vars, wrong Node version, etc. |
| `REFERENCE_ERROR` | No | Dangling references in user config |
| `VALIDATION_ERROR` | No | Schema/rule validation failures |
| `NETWORK_ERROR` | No | HTTP failures, timeouts |
| `AUTH_ERROR` | No | Authentication/authorization issues |
| `CONFIG_ERROR` | No | Invalid generators.yml, fern.config.json, etc. |

Only the first 5 codes are Sentry-reportable — they indicate bugs in Fern itself. The rest are user-actionable and would just create noise in Sentry.

### Simplifying error handling by removing redundant classes

- **Unified `CliError` across CLI v1 and v2.** CLI v2 had its own `CliError` class in `packages/cli/cli-v2/src/errors/CliError.ts`. This PR deletes it and makes both CLIs share the single `CliError` from `@fern-api/task-context`, ensuring consistent error codes and Sentry routing regardless of which CLI entry point is used.

- **Existing error classes now extend `CliError`.** `ValidationError`, `SourcedValidationError`, and `KeyringUnavailableError` now extend `CliError` with their appropriate codes (`VALIDATION_ERROR` and `AUTH_ERROR`). This means `resolveErrorCode()` handles them automatically — no special-casing needed in every catch block.

- **Removed `LoggableFernCliError`.** This was a wrapper that carried a log message alongside an error. With `CliError` now carrying a `message` field (it extends `Error`), the wrapper is redundant. All former `LoggableFernCliError` usages are replaced with `CliError`.

### `reportError` — single error reporting path (CLI v2)

CLI v2's `withContext.ts` previously had separate `shouldReportToSentry` and `extractErrorCode` functions. These are consolidated into a single `reportError(context, error, options?)` function that:
1. Skips `TaskAbortSignal` (already logged)
2. Resolves the error code via `resolveErrorCode()` (explicit override > `CliError.code` > fallback to `INTERNAL_ERROR`)
3. Reports to Sentry if the code is in `SENTRY_REPORTABLE_CODES`
4. Always reports to PostHog with the error code as a property

### Sentry tags

Error codes are now passed as Sentry tags (`errorCode`) on captured exceptions, making it possible to filter and alert on specific error categories in the Sentry dashboard.

## Commits (review in order)

1. **`add shared CliError class`** — introduces `CliError` in `@fern-api/task-context` with the code taxonomy, `shouldReportToSentry`, `resolveErrorCode`, and static factory methods
2. **`unify CLI v2 CliError`** — deletes CLI v2's local `CliError`, switches to shared one, introduces `reportError` in `withContext.ts`
3. **`clean up imports`** — mechanical import reordering for consistency
4. **`make error classes extend CliError`** — `ValidationError`, `SourcedValidationError`, `KeyringUnavailableError` now extend `CliError`
5. **`pass error code as Sentry tag`** — adds `errorCode` tag to `captureException` calls
6. **`remove LoggableFernCliError`** — replaces all usages with `CliError`, deletes the class

## Testing

- [x] Updated test helpers and mocks for new `CliError` / `MockTaskContext` signatures
- [x] Existing CLI v1 and v2 tests pass

<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/fern-api/fern/pull/14749" target="_blank">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
    <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants