Skip to content

Commit 924529a

Browse files
committed
refactor(cli): make ValidationError, SourcedValidationError, and KeyringUnavailableError extend CliError
Each subclass now carries a default error code (VALIDATION_ERROR / AUTH_ERROR), eliminating the need for the separate extractErrorCode and local shouldReportToSentry functions in withContext.ts. reportError now relies entirely on resolveErrorCode and the shared shouldReportToSentry from @fern-api/task-context. Made-with: Cursor
1 parent 5ea2661 commit 924529a

4 files changed

Lines changed: 30 additions & 45 deletions

File tree

packages/cli/cli-v2/src/auth/errors/KeyringUnavailableError.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import { CliError } from "@fern-api/task-context";
2+
13
/**
24
* Error thrown when the system keyring is unavailable.
35
*/
4-
export class KeyringUnavailableError extends Error {
6+
export class KeyringUnavailableError extends CliError {
57
public readonly platform: NodeJS.Platform;
6-
public readonly cause?: Error;
8+
public override readonly cause?: Error;
79

810
constructor(platform: NodeJS.Platform, cause?: Error) {
9-
super(getKeyringErrorMessage(platform));
11+
super({
12+
message: getKeyringErrorMessage(platform),
13+
code: "AUTH_ERROR"
14+
});
15+
Object.setPrototypeOf(this, KeyringUnavailableError.prototype);
1016
this.platform = platform;
1117
this.cause = cause;
1218
}

packages/cli/cli-v2/src/context/withContext.ts

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { LogLevel } from "@fern-api/logger";
2-
import {
3-
CliError,
4-
resolveErrorCode,
5-
shouldReportToSentry as shouldReportCodeToSentry,
6-
TaskAbortSignal
7-
} from "@fern-api/task-context";
2+
import { CliError, resolveErrorCode, shouldReportToSentry, TaskAbortSignal } from "@fern-api/task-context";
83

94
import chalk from "chalk";
105
import { KeyringUnavailableError } from "../auth/errors/KeyringUnavailableError.js";
@@ -108,44 +103,17 @@ function handleError(context: Context, error: unknown): void {
108103
process.stderr.write(`${chalk.red(String(error))}\n`);
109104
}
110105

111-
function shouldReportToSentry(error: unknown): boolean {
112-
if (error instanceof TaskAbortSignal) {
113-
return false;
114-
}
115-
if (error instanceof CliError) {
116-
return shouldReportCodeToSentry(error.code);
117-
}
118-
if (
119-
error instanceof ValidationError ||
120-
error instanceof SourcedValidationError ||
121-
error instanceof KeyringUnavailableError
122-
) {
123-
return false;
124-
}
125-
return true;
126-
}
127-
128-
function extractErrorCode(error: unknown): string {
129-
if (error instanceof CliError) {
130-
return error.code;
131-
}
132-
if (error instanceof ValidationError || error instanceof SourcedValidationError) {
133-
return "VALIDATION_ERROR";
134-
}
135-
if (error instanceof KeyringUnavailableError) {
136-
return "AUTH_ERROR";
137-
}
138-
return "INTERNAL_ERROR";
139-
}
140-
141106
/**
142107
* Reports an error to Sentry (conditionally) and PostHog.
143108
* Called from the top-level catch in withContext and from
144109
* TaskContextAdapter.failWithoutThrowing.
145110
*/
146111
export function reportError(context: Context, error: unknown, options?: { code?: CliError.Code }): void {
147-
const code = resolveErrorCode(error, options?.code) ?? extractErrorCode(error);
148-
if (shouldReportToSentry(error)) {
112+
if (error instanceof TaskAbortSignal) {
113+
return;
114+
}
115+
const code = resolveErrorCode(error, options?.code) ?? "INTERNAL_ERROR";
116+
if (shouldReportToSentry(code)) {
149117
context.telemetry.captureException(error);
150118
}
151119
context.telemetry.sendLifecycleEvent({
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CliError } from "@fern-api/task-context";
12
import { ValidationIssue } from "@fern-api/yaml-loader";
23

34
/**
@@ -6,11 +7,15 @@ import { ValidationIssue } from "@fern-api/yaml-loader";
67
* Used for fern.yml schema validation where each issue has a precise SourceLocation.
78
* When displayed, each issue is shown on its own line with file:line:col prefix.
89
*/
9-
export class SourcedValidationError extends Error {
10+
export class SourcedValidationError extends CliError {
1011
public readonly issues: ValidationIssue[];
1112

1213
constructor(issues: ValidationIssue[]) {
13-
super(issues.map((issue) => issue.toString()).join("\n"));
14+
super({
15+
message: issues.map((issue) => issue.toString()).join("\n"),
16+
code: "VALIDATION_ERROR"
17+
});
18+
Object.setPrototypeOf(this, SourcedValidationError.prototype);
1419
this.issues = issues;
1520
}
1621
}
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CliError } from "@fern-api/task-context";
2+
13
import type { ValidationViolation } from "./ValidationViolation.js";
24

35
/**
@@ -6,11 +8,15 @@ import type { ValidationViolation } from "./ValidationViolation.js";
68
* When displayed, each violation is shown on its own line with filepath prefix
79
* and severity-appropriate coloring.
810
*/
9-
export class ValidationError extends Error {
11+
export class ValidationError extends CliError {
1012
public readonly violations: ValidationViolation[];
1113

1214
constructor(violations: ValidationViolation[]) {
13-
super(violations.map((v) => `${v.relativeFilepath}: ${v.message}`).join("\n"));
15+
super({
16+
message: violations.map((v) => `${v.relativeFilepath}: ${v.message}`).join("\n"),
17+
code: "VALIDATION_ERROR"
18+
});
19+
Object.setPrototypeOf(this, ValidationError.prototype);
1420
this.violations = violations;
1521
}
1622
}

0 commit comments

Comments
 (0)