derailed/k9s · error
no valid selector found for job: %s
Error message
no valid selector found for job: %s
What it means
The Job log path converts the fetched Job and needs spec.selector.matchLabels to build the label selector that finds the Job's pods. A Job with a nil selector or zero matchLabels cannot be correlated to pods, so the DAO refuses to guess and errors, naming opts.Path.
Source
Thrown at internal/dao/job.go:89
return ll, nil
}
// TailLogs tail logs for all pods represented by this Job.
func (j *Job) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
o, err := j.getFactory().Get(j.gvr, opts.Path, true, labels.Everything())
if err != nil {
return nil, err
}
var job batchv1.Job
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
if err != nil {
return nil, errors.New("expecting a job resource")
}
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
return nil, fmt.Errorf("no valid selector found for job: %s", opts.Path)
}
return podLogs(ctx, job.Spec.Selector.MatchLabels, opts)
}
func (j *Job) GetInstance(fqn string) (*batchv1.Job, error) {
o, err := j.getFactory().Get(j.gvr, fqn, true, labels.Everything())
if err != nil {
return nil, err
}
var job batchv1.Job
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
if err != nil {
return nil, errors.New("expecting a job resource")
}
return &job, nilView on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Recreate the Job via kubectl/manifest so the API server generates the standard controller-uid selector
- Add an explicit spec.selector.matchLabels to the manifest matching spec.template.metadata.labels
- Workaround: fetch logs by selecting pods yourself with a known label or pod name instead of going through the Job
Example fix
# before (job.yaml, broken)
spec:
template:
spec:
containers:
- name: work
image: busybox
# after
spec:
selector:
matchLabels:
batch.kubernetes.io/controller-uid: work-uid
template:
metadata:
labels:
batch.kubernetes.io/controller-uid: work-uid
spec:
containers:
- name: work
image: busybox Defensive patterns
Strategy: validation
Validate before calling
job, err := jobDAO.GetInstance(fqn)
if err != nil { return err }
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
return fmt.Errorf("job %s has no pod selector; cannot fetch logs via job", fqn)
}
return jobDAO.Logs(ctx, LogOptions{Path: fqn}) Type guard
func hasPodSelector(j *batchv1.Job) bool {
return j.Spec.Selector != nil && len(j.Spec.Selector.MatchLabels) > 0
} Try / catch
if err := jobDAO.Logs(ctx, opts); err != nil {
if strings.Contains(err.Error(), "no valid selector found for job") {
// fall back to selecting pods manually or by name
}
} Prevention
- Always set spec.selector.matchLabels in Job manifests (let kubectl generate it)
- CI-validate applied Jobs with a policy check that spec.selector is non-empty
- For log tooling, accept a fallback explicit label selector from the caller
When it happens
Trigger: Requesting logs for a Job whose spec.selector is nil or whose matchLabels map is empty — hand-authored Job manifests missing the selector block, or Jobs created/mutated by controllers that strip selectors.
Common situations: Applying hand-written job.yaml without spec.selector (the API server normally injects batch.kubernetes.io/controller-uid, but direct writes or misbehaving admission webhooks can leave it empty); Jobs managed by custom operators that set their own ownership model.
Related errors
- expecting a job resource
- dialLogs - no connection to dial
- expecting a context factory
- expecting Job resource
- no factory in context
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/0d0e609d7b9e560a.
Report an issue: GitHub.