Skip to content

Commit 60eb2a2

Browse files
amcaplanclaude
andcommitted
Treat transient network errors as AbortError instead of BugError
Renames isARetryableNetworkError to isTransientNetworkError for clarity and exports it for reuse in fetchApiVersions error handling. This fixes 6,941+ production errors that were incorrectly classified as bugs. Changes: - Renamed isARetryableNetworkError -> isTransientNetworkError - Added JSDoc explaining these are transient network errors - Exported function for reuse across the codebase - Enhanced detection patterns to include TLS/cert errors, premature close, timeouts - Updated fetchApiVersions to classify network errors as AbortError Network errors now properly classified include: - TLS certificate validation failures (main issue with 6,941 occurrences) - Connection timeouts (ETIMEDOUT) - Connection resets (ECONNRESET) - DNS failures (ENOTFOUND, getaddrinfo) - Socket disconnects and premature closes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 22f3630 commit 60eb2a2

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/cli-kit': patch
3+
---
4+
5+
Treat transient network errors as user-facing AbortError instead of BugError in fetchApiVersions

packages/cli-kit/src/private/node/api.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ type VerboseResponse<T> =
6868
| CanRetryErrorResponse
6969
| UnauthorizedErrorResponse
7070

71-
function isARetryableNetworkError(error: unknown): boolean {
71+
/**
72+
* Checks if an error is a transient network error (connection issues, timeouts, DNS failures, TLS errors, etc.)
73+
* rather than an application logic error. These errors are typically:
74+
* - Worth retrying (when retry logic is configured)
75+
* - Should be reported as user-facing errors (AbortError) rather than bugs (BugError)
76+
*/
77+
export function isTransientNetworkError(error: unknown): boolean {
7278
if (error instanceof Error) {
7379
const networkErrorMessages = [
7480
'socket hang up',
@@ -82,6 +88,14 @@ function isARetryableNetworkError(error: unknown): boolean {
8288
'eai_again',
8389
'epipe',
8490
'the operation was aborted',
91+
'timeout',
92+
'premature close',
93+
'certificate',
94+
'cert',
95+
'tls',
96+
'ssl',
97+
'altnames',
98+
'getaddrinfo',
8599
]
86100
const errorMessage = error.message.toLowerCase()
87101
const anyMatches = networkErrorMessages.some((issueMessage) => errorMessage.includes(issueMessage))
@@ -105,7 +119,7 @@ async function runRequestWithNetworkLevelRetry<T extends {headers: Headers; stat
105119
return await requestOptions.request()
106120
} catch (err) {
107121
lastSeenError = err
108-
if (!isARetryableNetworkError(err)) {
122+
if (!isTransientNetworkError(err)) {
109123
throw err
110124
}
111125
outputDebug(`Retrying request to ${requestOptions.url} due to network error ${err}`)

packages/cli-kit/src/public/node/api/admin.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
restRequestUrl,
1515
isThemeAccessSession,
1616
} from '../../../private/node/api/rest.js'
17+
import {isTransientNetworkError} from '../../../private/node/api.js'
1718
import {RequestModeInput, shopifyFetch} from '../http.js'
1819
import {PublicApiVersions} from '../../../cli/api/graphql/admin/generated/public_api_versions.js'
1920

@@ -182,13 +183,25 @@ async function fetchApiVersions(session: AdminSession, preferredBehaviour?: Requ
182183
throw new AbortError(
183184
`Error connecting to your store ${session.storeFqdn}: ${error.message} ${error.response.status} ${error.response.data}`,
184185
)
185-
} else {
186-
throw new BugError(
187-
`Unknown error connecting to your store ${session.storeFqdn}: ${
186+
}
187+
188+
// Check for network-level errors (connection issues, timeouts, DNS failures, TLS errors, etc.)
189+
// These are transient errors that may have been retried already by lower-level retry logic
190+
if (isTransientNetworkError(error)) {
191+
throw new AbortError(
192+
`Network error connecting to your store ${session.storeFqdn}: ${
188193
error instanceof Error ? error.message : String(error)
189194
}`,
195+
'Check your internet connection and try again.',
190196
)
191197
}
198+
199+
// Unknown errors are likely bugs in the CLI
200+
throw new BugError(
201+
`Unknown error connecting to your store ${session.storeFqdn}: ${
202+
error instanceof Error ? error.message : String(error)
203+
}`,
204+
)
192205
}
193206
}
194207

0 commit comments

Comments
 (0)