@@ -18,6 +18,7 @@ import (
1818 "github.com/azure/azure-dev/cli/azd/pkg/extensions"
1919 "github.com/azure/azure-dev/cli/azd/pkg/input"
2020 "github.com/azure/azure-dev/cli/azd/pkg/ioc"
21+ "github.com/azure/azure-dev/cli/azd/pkg/output"
2122 "github.com/fatih/color"
2223)
2324
@@ -100,6 +101,8 @@ func (m *ExtensionsMiddleware) Run(ctx context.Context, next NextFn) (*actions.A
100101
101102 forceColor := ! color .NoColor
102103 var wg sync.WaitGroup
104+ var mu sync.Mutex
105+ var failedExtensions []* extensions.Extension
103106
104107 // Track total time for all extensions to become ready
105108 allExtensionsStartTime := time .Now ()
@@ -145,13 +148,19 @@ func (m *ExtensionsMiddleware) Run(ctx context.Context, next NextFn) (*actions.A
145148
146149 // Wait for the extension to signal readiness or failure.
147150 // If AZD_EXT_DEBUG is set to a truthy value, wait indefinitely for debugger attachment
151+ // If AZD_EXT_TIMEOUT is set to a number (seconds), use that as the timeout (default: 5 seconds)
148152 readyCtx , cancel := getReadyContext (ctx )
149153 defer cancel ()
150154
151155 startTime := time .Now ()
152156 if err := ext .WaitUntilReady (readyCtx ); err != nil {
153157 elapsed := time .Since (startTime )
154158 log .Printf ("'%s' extension failed to become ready after %v: %v\n " , ext .Id , elapsed , err )
159+
160+ // Track failed extensions for warning display
161+ mu .Lock ()
162+ failedExtensions = append (failedExtensions , ext )
163+ mu .Unlock ()
155164 } else {
156165 elapsed := time .Since (startTime )
157166 log .Printf ("'%s' extension became ready in %v\n " , ext .Id , elapsed )
@@ -162,6 +171,22 @@ func (m *ExtensionsMiddleware) Run(ctx context.Context, next NextFn) (*actions.A
162171 // Wait for all extensions to reach a terminal state (ready or failed)
163172 wg .Wait ()
164173
174+ // Check for failed extensions and display warnings
175+
176+ if len (failedExtensions ) > 0 {
177+ m .console .Message (ctx , output .WithWarningFormat ("WARNING: Extension startup failures detected" ))
178+ m .console .Message (ctx , "The following extensions failed to initialize within the timeout period:" )
179+ for _ , ext := range failedExtensions {
180+ m .console .Message (ctx , fmt .Sprintf (" - %s (%s)" , ext .DisplayName , ext .Id ))
181+ }
182+ m .console .Message (ctx , "" )
183+ m .console .Message (
184+ ctx ,
185+ "Some features may be unavailable. Increase timeout with AZD_EXT_TIMEOUT=<seconds> if needed." ,
186+ )
187+ m .console .Message (ctx , "" )
188+ }
189+
165190 // Log total time for all extensions to complete startup
166191 totalElapsed := time .Since (allExtensionsStartTime )
167192 log .Printf ("All %d extensions completed startup in %v\n " , len (extensionList ), totalElapsed )
@@ -185,5 +210,14 @@ func getReadyContext(ctx context.Context) (context.Context, context.CancelFunc)
185210 if isDebug () {
186211 return context .WithCancel (ctx )
187212 }
188- return context .WithTimeout (ctx , 5 * time .Second )
213+
214+ // Use custom timeout from environment variable or default to 5 seconds
215+ timeout := 5 * time .Second
216+ if timeoutValue := os .Getenv ("AZD_EXT_TIMEOUT" ); timeoutValue != "" {
217+ if seconds , err := strconv .Atoi (timeoutValue ); err == nil && seconds > 0 {
218+ timeout = time .Duration (seconds ) * time .Second
219+ }
220+ }
221+
222+ return context .WithTimeout (ctx , timeout )
189223}
0 commit comments