@@ -313,10 +313,9 @@ async def _init_stdio_python_server(self):
313313 # that is reconnecting is handled above and waits for availability.
314314 #
315315 # Set ``error_phase = BOX_UNAVAILABLE`` BEFORE raising so the retry
316- # wrapper can short-circuit (retrying is pointless when Box is
317- # deliberately off) and the frontend can render a localized,
318- # actionable message instead of this raw RuntimeError. Keep the
319- # message itself short — the frontend ignores it for this phase.
316+ # wrapper can distinguish a deliberately disabled Box from an enabled
317+ # runtime that is still reconnecting. Keep the message itself short —
318+ # the frontend ignores it for this phase.
320319 box_service = getattr (self .ap , 'box_service' , None )
321320 if box_service is not None and not getattr (box_service , 'available' , False ):
322321 self .error_phase = MCPSessionErrorPhase .BOX_UNAVAILABLE
@@ -620,17 +619,29 @@ async def _lifecycle_loop_with_retry(self):
620619 await asyncio .sleep (2 )
621620 continue
622621 except Exception as e :
623- self .retry_count = attempt + 1
624622 if self ._shutdown_event .is_set ():
625623 return # Shutdown requested, don't retry
626- # BOX_UNAVAILABLE is a deliberate refusal, not a transient
627- # failure — retrying produces log spam and a misleading
628- # "Failed after N attempts" message. Surface it immediately.
629624 if self .error_phase == MCPSessionErrorPhase .BOX_UNAVAILABLE :
625+ box_service = getattr (self .ap , 'box_service' , None )
626+ if box_service is not None and getattr (box_service , 'enabled' , True ):
627+ # Box is configured and may recover independently of
628+ # this MCP session. Keep retrying without consuming the
629+ # fatal budget; _wait_for_box_runtime() rate-limits the
630+ # loop to one warning per startup timeout.
631+ self .status = MCPSessionStatus .CONNECTING
632+ self .error_message = None
633+ self .error_phase = None
634+ await asyncio .sleep (1 )
635+ continue
636+ # Explicitly disabled Box is a deliberate refusal, not a
637+ # transient failure. Surface it immediately without log
638+ # spam or a misleading "Failed after N attempts" message.
639+ self .retry_count = attempt + 1
630640 self .status = MCPSessionStatus .ERROR
631641 self .error_message = str (e )
632642 self ._ready_event .set ()
633643 return
644+ self .retry_count = attempt + 1
634645 if attempt >= self ._MAX_RETRIES :
635646 self .status = MCPSessionStatus .ERROR
636647 self .error_message = f'Failed after { self ._MAX_RETRIES + 1 } attempts: { self ._describe_exception (e )} '
0 commit comments