@@ -37,6 +37,16 @@ function isGenAIMessageCaptureEnabled(): boolean {
3737// flavor). Callers suppress ERROR status on cancel paths.
3838export function isCancellationError ( err : unknown ) : boolean {
3939 if ( err == null ) return false
40+ // `controller.abort(reason)` where `reason` is a plain string
41+ // rejects fetch() with that string directly, not a DOMException.
42+ // Our abort taxonomy uses `user_stop:*`, `redis_abort_marker:*`,
43+ // `timeout:*` reason prefixes (see `request/session/abort.ts`), so
44+ // treat those strings — and any fetch-abort-flavored message — as
45+ // cancellation. Without this branch, a user Stop turns into a
46+ // span with status=error even though nothing went wrong.
47+ if ( typeof err === 'string' ) {
48+ return / a b o r t e d | A b o r t E r r o r | ^ u s e r _ s t o p : | ^ r e d i s _ a b o r t _ m a r k e r : | ^ t i m e o u t : / i. test ( err )
49+ }
4050 if ( typeof err === 'object' ) {
4151 const e = err as { name ?: unknown ; code ?: unknown ; message ?: unknown }
4252 if ( e . name === 'AbortError' ) return true
@@ -49,6 +59,24 @@ export function isCancellationError(err: unknown): boolean {
4959 return false
5060}
5161
62+ /**
63+ * True iff an HTTP response status code represents a real server-side
64+ * problem (5xx) or a user-visible condition we want to alert on
65+ * (402 Payment Required, 409 Conflict, 429 Too Many Requests).
66+ *
67+ * Everything else — in particular the 4xx flood from bot probes and
68+ * expected auth/validation rejections — stays UNSET on the span so
69+ * dashboards don't treat normal rejections as errors.
70+ *
71+ * Mirrored on the Go side in
72+ * `copilot/internal/http/middleware/telemetry.go`. Keep the two in
73+ * sync if you change the actionable set.
74+ */
75+ export function isActionableErrorStatus ( code : number ) : boolean {
76+ if ( code >= 500 ) return true
77+ return code === 402 || code === 409 || code === 429
78+ }
79+
5280// Record exception + set ERROR only for real failures (cancels stay unset).
5381export function markSpanForError ( span : Span , error : unknown ) : void {
5482 const asError = error instanceof Error ? error : new Error ( String ( error ) )
0 commit comments