@@ -4,7 +4,10 @@ import {Organization, MinimalOrganizationApp, OrganizationApp} from '../../model
44import { getCachedCommandInfo , setCachedCommandTomlPreference } from '../local-storage.js'
55import { CreateAppOptions , DeveloperPlatformClient } from '../../utilities/developer-platform-client.js'
66import { AppConfigurationFileName } from '../../models/app/loader.js'
7- import { BugError } from '@shopify/cli-kit/node/error'
7+ import { AbortError , BugError } from '@shopify/cli-kit/node/error'
8+ import { outputInfo , outputDebug } from '@shopify/cli-kit/node/output'
9+
10+ const MAX_PROMPT_RETRIES = 2
811
912/**
1013 * Select an app from env, list or create a new one:
@@ -30,25 +33,89 @@ export async function selectOrCreateApp(
3033 const name = await appNamePrompt ( options . name )
3134 return developerPlatformClient . createApp ( org , { ...options , name} )
3235 } else {
33- const app = await selectAppPrompt ( searchForAppsByNameFactory ( developerPlatformClient , org . id ) , apps , hasMorePages , {
34- directory : options . directory ,
35- } )
36+ // Capture app selection context
37+ const cachedData = getCachedCommandInfo ( )
38+ const tomls = ( cachedData ?. tomls as { [ key : string ] : AppConfigurationFileName } ) ?? { }
39+
40+ for ( let attempt = 0 ; attempt < MAX_PROMPT_RETRIES ; attempt ++ ) {
41+ try {
42+ // eslint-disable-next-line no-await-in-loop
43+ const app = await selectAppPrompt (
44+ searchForAppsByNameFactory ( developerPlatformClient , org . id ) ,
45+ apps ,
46+ hasMorePages ,
47+ { directory : options . directory } ,
48+ )
49+
50+ const selectedToml = tomls [ app . apiKey ]
51+ if ( selectedToml ) setCachedCommandTomlPreference ( selectedToml )
52+
53+ // eslint-disable-next-line no-await-in-loop
54+ const fullSelectedApp = await developerPlatformClient . appFromIdentifiers ( app . apiKey )
55+
56+ if ( ! fullSelectedApp ) {
57+ throw new BugError (
58+ `Unable to fetch app ${ app . apiKey } from Shopify` ,
59+ 'Try running `shopify app config link` to connect to an app you have access to.' ,
60+ )
61+ }
3662
37- const data = getCachedCommandInfo ( )
38- const tomls = ( data ?. tomls as { [ key : string ] : AppConfigurationFileName } ) ?? { }
39- const selectedToml = tomls [ app . apiKey ]
63+ return fullSelectedApp
64+ } catch ( error ) {
65+ // Don't retry BugError - those indicate actual bugs, not transient issues
66+ if ( error instanceof BugError ) {
67+ throw error
68+ }
4069
41- if ( selectedToml ) setCachedCommandTomlPreference ( selectedToml )
70+ const errorObj = error as Error
4271
43- const fullSelectedApp = await developerPlatformClient . appFromIdentifiers ( app . apiKey )
72+ // Log each attempt for observability
73+ outputDebug ( `App selection attempt ${ attempt + 1 } /${ MAX_PROMPT_RETRIES } failed: ${ errorObj . message } ` )
4474
45- if ( ! fullSelectedApp ) {
46- // This is unlikely, and a bug. But we still want a nice user facing message plus appropriate context logged.
47- throw new BugError (
48- `Unable to fetch app ${ app . apiKey } from Shopify` ,
49- 'Try running `shopify app config link` to connect to an app you have access to.' ,
50- )
75+ // If we have retries left, inform user and retry
76+ if ( attempt < MAX_PROMPT_RETRIES - 1 ) {
77+ outputInfo ( 'App selection was interrupted. Retrying...' )
78+ } else {
79+ const tryMessage = [
80+ 'This may happen if:' ,
81+ ' • The process received a signal (SIGINT, SIGTERM) or was terminated' ,
82+ ' • Running in an unstable environment (container restart, resource limits)' ,
83+ ' • Network interruption during app fetching' ,
84+ '' ,
85+ 'Try running the command again. If the issue persists:' ,
86+ ' • Check system resources and stability' ,
87+ ' • Try running outside of containers/WSL if applicable' ,
88+ ' • Report this issue with the error detailsmad a verbose log' ,
89+ ]
90+ . filter ( Boolean )
91+ . join ( '\n' )
92+
93+ throw new BugError ( errorObj . message , tryMessage )
94+ }
95+ }
5196 }
52- return fullSelectedApp
97+
98+ // User-facing error message with key diagnostic info
99+ const errorMessage = [
100+ 'Unable to select an app: the selection prompt was interrupted multiple times.' ,
101+ '' ,
102+ `Available apps: ${ apps . length } ` ,
103+ ] . join ( '\n' )
104+
105+ const tryMessage = [
106+ 'This may happen if:' ,
107+ ' • The process received a signal (SIGINT, SIGTERM) or was terminated' ,
108+ ' • Running in an unstable environment (container restart, resource limits)' ,
109+ ' • Network interruption during app fetching' ,
110+ '' ,
111+ 'Try running the command again. If the issue persists:' ,
112+ ' • Check system resources and stability' ,
113+ ' • Try running outside of containers/WSL if applicable' ,
114+ ' • Report this issue with the error details' ,
115+ ]
116+ . filter ( Boolean )
117+ . join ( '\n' )
118+
119+ throw new BugError ( errorMessage , tryMessage )
53120 }
54121}
0 commit comments