fix(ios): recognise every shape a blown launch bound arrives in

The runner transport reports a blown budget two ways, its own comment says
so: the context's error once cancellation has landed, and the connection's
i/o timeout when the deadline armed from that context fires first. The
legacy transport reports it as a gRPC status. errors.Is against
context.DeadlineExceeded only matches the first, so the session restart
never fired for the other two and a wedged session stayed wedged.
This commit is contained in:
pj committed 2026-08-16 01:00:45 +05:30
1 parent 71134a3347
commit f1a5db886e
1 file changed
+21 -1
+21 -1
View File
@@ -484,6 +484,26 @@ func isConnectionError(err error) bool {
return false return false
} }
// isBudgetExpiry reports whether err is a call that outlived its bound rather
// than a failure the transport can name. Each transport says so differently:
// the runner wraps the context's error when cancellation has landed and the
// connection's i/o timeout when the deadline it armed from that context fires
// first, and the legacy companion returns a gRPC status. Only an expiry earns
// a session restart; an error the runner reports has already said what a fresh
// session would say.
func isBudgetExpiry(err error) bool {
if err == nil {
return false
}
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, os.ErrDeadlineExceeded) {
return true
}
if statusValue, ok := status.FromError(err); ok {
return statusValue.Code() == codes.DeadlineExceeded
}
return false
}
func (d *Driver) Launch(ctx context.Context, bundleID string, clearState bool, env map[string]string) error { func (d *Driver) Launch(ctx context.Context, bundleID string, clearState bool, env map[string]string) error {
if bundleID != "" { if bundleID != "" {
d.bundleID = bundleID d.bundleID = bundleID
@@ -545,7 +565,7 @@ func (d *Driver) launchWithSessionRecovery(ctx context.Context) error {
err := d.lifecycleCall(ctx, launch) err := d.lifecycleCall(ctx, launch)
// A caller whose own budget ran out gets no restart: the bound that expired // A caller whose own budget ran out gets no restart: the bound that expired
// was the caller's to spend, and the second attempt would inherit it dead. // was the caller's to spend, and the second attempt would inherit it dead.
if err == nil || !errors.Is(err, context.DeadlineExceeded) || ctx.Err() != nil || d.restart == nil { if !isBudgetExpiry(err) || ctx.Err() != nil || d.restart == nil {
return err return err
} }
fmt.Fprintf(d.output, "launch %s blew its %v bound (%v); restarting the session and launching once more\n", fmt.Fprintf(d.output, "launch %s blew its %v bound (%v); restarting the session and launching once more\n",