{"record":{"id":"702b17e8084f0e9c","repo":"goharbor/harbor","slug":"unknown-status","errorCode":null,"errorMessage":"unknown status","messagePattern":"unknown status","errorType":"http","errorClass":"lib/errors.Error","httpStatus":200,"severity":"info","filePath":"src/controller/health/checker.go","lineNumber":97,"sourceCode":"\tu.Lock()\n\tdefer u.Unlock()\n\n\treturn u.status\n}\n\nfunc (u *updater) update(status error) {\n\tu.Lock()\n\tdefer u.Unlock()\n\n\tu.status = status\n}\n\n// PeriodicHealthChecker implements a Checker to check status periodically\nfunc PeriodicHealthChecker(checker health.Checker, period time.Duration) health.Checker {\n\tu := &updater{\n\t\t// init the \"status\" as \"unknown status\" error to avoid returning nil error(which means healthy)\n\t\t// before the first health check request finished\n\t\tstatus: errors.New(\"unknown status\"),\n\t}\n\n\tgo func() {\n\t\tticker := time.NewTicker(period)\n\t\tfor {\n\t\t\tu.update(checker.Check())\n\t\t\t<-ticker.C\n\t\t}\n\t}()\n\n\treturn u\n}\n\nfunc coreHealthChecker() health.Checker {\n\treturn health.CheckFunc(func() error {\n\t\treturn nil\n\t})\n}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/controller/health/checker.go#L79-L115","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wait one check period (or add a startup probe / initialDelay) before treating the component as failed.","Verify the underlying dependency the checker contacts is reachable so the first tick can succeed.","Treat 'unknown status' as not-yet-ready, distinct from unhealthy, in probe logic."],"exampleFix":"// before\nif err := component.Health(); err != nil { restart() }\n\n// after\nif err := component.Health(); err != nil {\n    if err.Error() == \"unknown status\" { waitOnePeriod(); continue }\n    restart()\n}","handlingStrategy":"retry","validationCode":"// probe readiness before acting on health status:\nfunc ready(h health.Checker, period time.Duration) bool {\n    deadline := time.Now().Add(2 * period)\n    for time.Now().Before(deadline) {\n        if err := h.Check(); err == nil || err.Error() != \"unknown status\" { return true }\n        time.Sleep(time.Second)\n    }\n    return false\n}","typeGuard":"func isUnknownStatus(err error) bool {\n    return err != nil && err.Error() == \"unknown status\"\n}","tryCatchPattern":"if err := comp.Check(); err != nil {\n    if err.Error() == \"unknown status\" {\n        // not yet initialized: back off one period and re-check\n        time.Sleep(period)\n        err = comp.Check()\n    }\n    if err != nil { /* now a real failure */ }\n}","preventionTips":["Give components a startup grace window (startup probe / initialDelay) longer than the check period.","Ensure the checked dependency is reachable so the first tick succeeds.","Distinguish 'unknown' from 'unhealthy' in alerting to avoid restart storms."],"tags":["health","startup","sentinel","concurrency"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}