kataras/iris · error

failed to connect to the server after %d retries

Error message

failed to connect to the server after %d retries

What it means

Application.tryConnect retries connecting to the configured server address up to maxRetries with exponential backoff (base^i seconds); if every attempt fails it returns 'failed to connect to the server after %d retries'. This is surfaced through app.Wait() when a caller waits for a server started asynchronously.

Source

Thrown at iris.go:1197

			// Increase the retry interval by the base raised to the power of the number of attempts.
			/*
				0	2 seconds
				1	4 seconds
				2	8 seconds
				3	~16 seconds
				4	~32 seconds
				5	~64 seconds
				6	~128 seconds
				7	~256 seconds
				8	~512 seconds
				...
			*/
			retryInterval = time.Duration(math.Pow(base, float64(i+1))) * time.Second
		}
	}
	// All attempts failed, return an error.
	return fmt.Errorf("failed to connect to the server after %d retries", maxRetries)
}

// https://ngrok.com/docs
func (app *Application) tryStartTunneling() {
	if len(app.config.Tunneling.Tunnels) == 0 {
		return
	}

	app.ConfigureHost(func(su *host.Supervisor) {
		su.RegisterOnServe(func(h host.TaskHost) {
			publicAddrs, err := tunnel.Start(app.config.Tunneling)
			if err != nil {
				app.logger.Errorf("Host: tunneling error: %v", err)
				return
			}

			publicAddr := publicAddrs[0]
			// to make subdomains resolution still based on this new remote, public addresses.

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Check the server actually started: look for earlier errors from app.Run (e.g. 'listen tcp: address already in use').
  2. Verify the configured address/host matches where the server binds (localhost vs container hostname).
  3. Increase the retry budget or wait interval if the service legitimately starts slowly.
  4. Check firewall/security-group rules for the port.
  5. In containers, use readiness probes / wait-for scripts instead of relying solely on tryConnect retries.

Example fix

// before
app.Run(iris.Addr(":8080"))
go app.Wait()
// after: run and wait in the same flow, or check startup error first
if err := app.Listen(":8080", iris.WithoutStartupLog); err != nil {
    log.Fatalf("server failed to start: %v", err)
}
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil {
    log.Printf("target %s not reachable yet: %v", addr, err)
} else { conn.Close() }

Try / catch

go app.Run(iris.Addr(addr))
if err := app.Wait(); err != nil {
    if strings.Contains(err.Error(), "failed to connect") {
        log.Fatalf("server at %s never came up: %v", addr, err) // check server logs for bind errors
    }
    log.Fatal(err)
}

Prevention

When it happens

Trigger: Calling app.Wait() after app.Run/Spawn in a goroutine while the listener never becomes reachable within maxRetries * exponential intervals — e.g. app.Listen failed, wrong host/port configured, firewall blocking, or the server crashed during startup.

Common situations: Port already in use so the server never starts; connecting to 0.0.0.0/localhost mismatch inside Docker/Kubernetes before the pod is ready; firewalled ports; slow startup exceeding the retry budget (each retry grows as base^(i+1) seconds).

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/9760c28597091d17. Report an issue: GitHub.