goharbor/harbor · warning · lib/errors.Error

NOT_FOUND

NOT_FOUND

Error message

no schedule is found

What it means

The jobservice schedule controller's Get lists schedules filtered only by the caller-supplied vendorType. If no schedule exists for that vendor (job never scheduled through this API), it returns NOT_FOUND 'no schedule is found' - the expected state before the first Create call for that vendor.

Source

Thrown at src/controller/jobservice/schedule.go:70

	queueStatusMgr        queuestatus.Manager
}

// NewSchedulerCtrl ...
func NewSchedulerCtrl() SchedulerController {
	return &schedulerController{
		schedulerMgr:          scheduler.New(),
		jobServiceRedisClient: jm.JobServiceRedisClient,
		queueStatusMgr:        queuestatus.Mgr,
	}
}

func (s *schedulerController) Get(ctx context.Context, vendorType string) (*scheduler.Schedule, error) {
	sch, err := s.schedulerMgr.ListSchedules(ctx, q.New(q.KeyWords{"VendorType": vendorType}))
	if err != nil {
		return nil, err
	}
	if len(sch) == 0 {
		return nil, errors.New(nil).WithCode(errors.NotFoundCode).WithMessage("no schedule is found")
	}
	if sch[0] == nil {
		return nil, errors.New(nil).WithCode(errors.NotFoundCode).WithMessage("no schedule is found")
	}
	return sch[0], nil
}

func (s *schedulerController) Create(ctx context.Context, vendorType, cronType, cron, callbackFuncName string,
	policy any, extrasParam map[string]any) (int64, error) {
	return s.schedulerMgr.Schedule(ctx, vendorType, -1, cronType, cron, callbackFuncName, policy, extrasParam)
}

func (s *schedulerController) Delete(ctx context.Context, vendorType string) error {
	return s.schedulerMgr.UnScheduleByVendor(ctx, vendorType, -1)
}

func (s *schedulerController) List(ctx context.Context, query *q.Query) ([]*scheduler.Schedule, error) {
	return s.schedulerMgr.ListSchedules(ctx, query)

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Create the schedule for the vendor type first (schedulerController.Create with cron spec).
  2. Treat 404 as 'not scheduled' and branch accordingly in the client.
  3. Confirm you are querying the right vendorType string.

Example fix

// before
sch, err := schedCtl.Get(ctx, vendorType)

// after
sch, err := schedCtl.Get(ctx, vendorType)
if liberrors.IsErr(err, liberrors.NotFoundCode) {
    _, err = schedCtl.Create(ctx, vendorType, scheduler.Hourly, "0 0 * * * *", callback, nil, nil)
}
Defensive patterns

Strategy: fallback

Validate before calling

sch, err := schedCtl.Get(ctx, vendorType)
if liberrors.IsErr(err, liberrors.NotFoundCode) {
    sch = nil // interpret as "no schedule for this vendor yet"
}

Try / catch

sch, err := s.Get(ctx, vendorType)
if err != nil {
    if !liberrors.IsErr(err, liberrors.NotFoundCode) { return nil, err }
    _, err = s.Create(ctx, vendorType, scheduler.Daily, "0 0 0 * * *", cb, nil, nil)
    if err != nil { return nil, err }
    sch, err = s.Get(ctx, vendorType)
}

Prevention

When it happens

Trigger: GET on the schedule endpoint for a vendor type (e.g. audit log forwarder, purge jobs) before any schedule was created for it; after the vendor's schedule was deleted.

Common situations: Automation reading schedule state before creating it; feature optional and never enabled; schedule removed by cleanup tooling.

Related errors


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