goharbor/harbor · info · lib/errors.Error

unknown status

Error message

unknown status

What it means

PeriodicHealthChecker wraps a checker with a background ticker and deliberately initializes status to errors.New("unknown status") so the component does not look healthy before the first Check() completes. The value is an internal sentinel (no error code); it surfaces through /api/v2/health style component status until the first tick overwrites it.

Source

Thrown at src/controller/health/checker.go:97

	u.Lock()
	defer u.Unlock()

	return u.status
}

func (u *updater) update(status error) {
	u.Lock()
	defer u.Unlock()

	u.status = status
}

// PeriodicHealthChecker implements a Checker to check status periodically
func PeriodicHealthChecker(checker health.Checker, period time.Duration) health.Checker {
	u := &updater{
		// init the "status" as "unknown status" error to avoid returning nil error(which means healthy)
		// before the first health check request finished
		status: errors.New("unknown status"),
	}

	go func() {
		ticker := time.NewTicker(period)
		for {
			u.update(checker.Check())
			<-ticker.C
		}
	}()

	return u
}

func coreHealthChecker() health.Checker {
	return health.CheckFunc(func() error {
		return nil
	})
}

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Wait one check period (or add a startup probe / initialDelay) before treating the component as failed.
  2. Verify the underlying dependency the checker contacts is reachable so the first tick can succeed.
  3. Treat 'unknown status' as not-yet-ready, distinct from unhealthy, in probe logic.

Example fix

// before
if err := component.Health(); err != nil { restart() }

// after
if err := component.Health(); err != nil {
    if err.Error() == "unknown status" { waitOnePeriod(); continue }
    restart()
}
Defensive patterns

Strategy: retry

Validate before calling

// probe readiness before acting on health status:
func ready(h health.Checker, period time.Duration) bool {
    deadline := time.Now().Add(2 * period)
    for time.Now().Before(deadline) {
        if err := h.Check(); err == nil || err.Error() != "unknown status" { return true }
        time.Sleep(time.Second)
    }
    return false
}

Type guard

func isUnknownStatus(err error) bool {
    return err != nil && err.Error() == "unknown status"
}

Try / catch

if err := comp.Check(); err != nil {
    if err.Error() == "unknown status" {
        // not yet initialized: back off one period and re-check
        time.Sleep(period)
        err = comp.Check()
    }
    if err != nil { /* now a real failure */ }
}

Prevention

When it happens

Trigger: Querying component health within one check period of process start; the wrapped Check() blocking forever so the status never updates; a very long period configured.

Common situations: Harbor core/jobservice just restarted and orchestrators probe health immediately; the checked dependency (DB, registry) is slow to accept connections; probes with no startup grace window.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/702b17e8084f0e9c. Report an issue: GitHub.