goharbor/harbor · error

nil stats body

Error message

nil stats body

What it means

job.Stats.Validate (src/jobservice/job/models.go:118) rejects stats lacking the embedded Info object (JSON 'info'). Stats records are persisted to the worker backend and validated on creation/read; a payload without 'info' means the record was truncated, corrupt, or hand-built incorrectly.

Source

Thrown at src/jobservice/job/models.go:118

// StatusChange is designed for reporting the status change via hook.
type StatusChange struct {
	JobID    string     `json:"job_id"`
	Status   string     `json:"status"`
	CheckIn  string     `json:"check_in,omitempty"`
	Metadata *StatsInfo `json:"metadata,omitempty"`
}

// SimpleStatusChange only keeps job ID and the target status
type SimpleStatusChange struct {
	JobID        string `json:"job_id"`
	TargetStatus string `json:"target_status"`
	Revision     int64  `json:"revision"`
}

// Validate the job stats
func (st *Stats) Validate() error {
	if st.Info == nil {
		return errors.New("nil stats body")
	}

	if utils.IsEmptyStr(st.Info.JobID) {
		return errors.New("missing job ID in job stats")
	}

	if utils.IsEmptyStr(st.Info.JobName) {
		return errors.New("missing job name in job stats")
	}

	if utils.IsEmptyStr(st.Info.JobKind) {
		return errors.New("missing job name in job stats")
	}

	if st.Info.JobKind != KindGeneric &&
		st.Info.JobKind != KindPeriodic &&
		st.Info.JobKind != KindScheduled {
		return errors.Errorf("job kind is not supported: %s", st.Info.JobKind)

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. When creating stats, always populate Info with job_id, job_name, and job_kind
  2. For corrupt persisted stats, delete/recreate the job entry rather than bypassing validation
  3. Keep producer and consumer Harbor versions aligned on the job.Stats schema

Example fix

// before
st := &job.Stats{}
// after
st := &job.Stats{
    Info: &job.StatsInfo{
        JobID:   "ju-1234",
        JobName: "GARBAGE_COLLECTION",
        JobKind: job.KindGeneric,
    },
}
Defensive patterns

Strategy: type-guard

Validate before calling

if err := st.Validate(); err != nil {
    return err // fail before persisting or forwarding stats
}

Type guard

func hasStatsInfo(st *job.Stats) bool {
    return st != nil && st.Info != nil
}

Prevention

When it happens

Trigger: json.Unmarshal of a payload like {} or {"revision":1} into job.Stats followed by Validate; a manager reading a Redis/DB stats entry whose info JSON was written by an incompatible Harbor version.

Common situations: Harbor version upgrades with stats schema drift; sharing a Redis instance across Harbor versions; manually crafted test fixtures; partially migrated job records.

Related errors


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