gofiber/fiber · warning

service %s terminate: %w

Error message

service %s terminate: %w

What it means

Returned by shutdownServices (services.go:128) when the shutdown context is already canceled/deadline-exceeded before a service's Terminate is called. Fiber still attempts best-effort termination of remaining services (it appends the error and continues rather than returning), wrapping ctx.Err() with the service name.

Source

Thrown at services.go:128

	return errors.Join(errs...)
}

// shutdownServices Handles the shutdown process of services for the current application.
// Iterates over all the started services in reverse order and tries to terminate them,
// returning an error if any error occurs.
func (app *App) shutdownServices(ctx context.Context) error {
	if app.state.ServicesLen() == 0 {
		return nil
	}

	var errs []error
	for key, srv := range app.state.Services() {
		if srv == nil {
			return fmt.Errorf("fiber: service %q is nil", key)
		}
		if err := ctx.Err(); err != nil {
			// Context is canceled, do a best effort to terminate the services.
			errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
			continue
		}

		err := srv.Terminate(ctx)
		if err != nil {
			// Best effort to terminate the services.
			errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
			continue
		}

		// Remove the service from the State
		app.state.deleteService(srv)
	}
	return errors.Join(errs...)
}

// logServices logs information about services and returns an error
// if any configured service is nil.

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Give app.Shutdown a generous deadline (e.g. context.WithTimeout(context.Background(), 30*time.Second)).
  2. Configure ServicesShutdownContextProvider to return a context with adequate remaining time.
  3. Make Service.Terminate fast and idempotent so it completes within the deadline.
  4. Avoid double-canceling the shutdown context from signal handlers.

Example fix

// before: tight shutdown deadline
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
_ = app.ShutdownWithContext(ctx)

// after: deadline sized to slowest service drain
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.ShutdownWithContext(ctx); err != nil {
    log.Printf("shutdown completed with errors: %v", err)
}
Defensive patterns

Strategy: validation

Validate before calling

shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = app.ShutdownWithContext(shutdownCtx)

Try / catch

if err := app.ShutdownWithContext(shutdownCtx); err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        log.Printf("shutdown context expired: %v", err)
    }
}

Prevention

When it happens

Trigger: Calling app.Shutdown with an already-canceled or short-deadline context, or the shutdown context being canceled mid-loop (e.g. SIGINT during graceful shutdown, or Config.ServicesShutdownContextProvider returning an expired context). The ctx.Err() check at services.go:126 fires.

Common situations: Shutdown deadline shorter than the time needed to drain in-flight requests/close DB pools; double SIGINT forcing immediate cancel; orchestrator hard-killing before graceful drain completes.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/48358136d5f20aa6.json. Report an issue: GitHub.