Skip to content

Commit d09aafc

Browse files
committed
fix: preserve ApiError type in Seer error handler + suggest trial start command
Two fixes for the highest-impact error classes: 1. handleSeerApiError: return ApiError instead of plain Error for non-Seer API failures (CLI-N, 84 users). Previously, unrecognized 403s and 500s from the Seer endpoint became plain Error objects, losing status code and endpoint info. Now preserves them as ApiError. 2. SeerError.format(): suggest 'sentry trial start seer <org>' instead of passive 'sentry trial list' when org is known (CLI-1D/BW/98, 237 users combined). The interactive trial prompt in bin.ts requires isatty(0) which is false for AI agents and CI — the most common callers of issue explain/plan. The direct command gives non-interactive users an actionable next step they can copy-paste.
1 parent 0963e66 commit d09aafc

File tree

2 files changed

+17
-9
lines changed

2 files changed

+17
-9
lines changed

src/lib/errors.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -404,11 +404,14 @@ export class SeerError extends CliError {
404404
}
405405

406406
override format(): string {
407-
// Soften trial hint — we can't check availability synchronously,
408-
// so use "check" language rather than "start" to avoid misleading
409-
// users whose trial is already expired
410-
const trialHint =
411-
"\n\nYou may be eligible for a free trial:\n sentry trial list";
407+
// When org slug is known, suggest the direct trial start command.
408+
// The interactive trial prompt in bin.ts handles TTY users; this
409+
// message is the fallback for non-interactive contexts (AI agents, CI)
410+
// where the prompt can't fire — hence the direct command suggestion
411+
// (CLI-1D/BW/98, 237 users combined).
412+
const trialHint = this.orgSlug
413+
? `\n\nStart a free trial:\n sentry trial start seer ${this.orgSlug}`
414+
: "\n\nYou may be eligible for a free trial:\n sentry trial list";
412415

413416
// When org slug is known, provide direct URLs to settings
414417
if (this.orgSlug) {

src/lib/formatters/seer.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010
RootCause,
1111
SolutionArtifact,
1212
} from "../../types/seer.js";
13-
import { SeerError } from "../errors.js";
13+
import { ApiError, SeerError } from "../errors.js";
1414
import { cyan } from "./colors.js";
1515
import { escapeMarkdownInline, renderMarkdown } from "./markdown.js";
1616

@@ -195,12 +195,17 @@ export function createSeerError(
195195
}
196196

197197
/**
198-
* Convert an API error to a Seer-specific error or a generic error.
198+
* Convert an API error to a Seer-specific error or an ApiError.
199+
*
200+
* Returns a SeerError for known Seer issues (402/403 with specific detail),
201+
* or preserves the error as an ApiError for other failures. Previously
202+
* returned a plain Error which lost the status code and endpoint — causing
203+
* CLI-N (84 users) where unrecognized 403s became untyped errors.
199204
*
200205
* @param status - HTTP status code
201206
* @param detail - Error detail from API
202207
* @param orgSlug - Organization slug for constructing settings URLs
203-
* @returns SeerError for Seer-specific errors, or a generic Error for other API errors
208+
* @returns SeerError for Seer-specific errors, or ApiError for other API errors
204209
*/
205210
export function handleSeerApiError(
206211
status: number,
@@ -211,7 +216,7 @@ export function handleSeerApiError(
211216
if (seerError) {
212217
return seerError;
213218
}
214-
return new Error(formatAutofixError(status, detail));
219+
return new ApiError(formatAutofixError(status, detail), status, detail);
215220
}
216221

217222
/**

0 commit comments

Comments
 (0)