{"record":{"id":"942d2c4221599711","repo":"derailed/k9s","slug":"expecting-a-job-resource","errorCode":null,"errorMessage":"expecting a job resource","messagePattern":"expecting a job resource","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/job.go","lineNumber":85,"sourceCode":"\t\t\t\tll = append(ll, o)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ll, nil\n}\n\n// TailLogs tail logs for all pods represented by this Job.\nfunc (j *Job) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {\n\to, err := j.getFactory().Get(j.gvr, opts.Path, true, labels.Everything())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar job batchv1.Job\n\terr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)\n\tif err != nil {\n\t\treturn nil, errors.New(\"expecting a job resource\")\n\t}\n\n\tif job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {\n\t\treturn nil, fmt.Errorf(\"no valid selector found for job: %s\", opts.Path)\n\t}\n\n\treturn podLogs(ctx, job.Spec.Selector.MatchLabels, opts)\n}\n\nfunc (j *Job) GetInstance(fqn string) (*batchv1.Job, error) {\n\to, err := j.getFactory().Get(j.gvr, fqn, true, labels.Everything())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar job batchv1.Job\n\terr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)\n\tif err != nil {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/job.go#L67-L103","documentation":"Job.TailLogs fetches one Job from the informer cache and converts it into batchv1.Job to read its selector before delegating to podLogs (internal/dao/job.go:85). If conversion fails, the sentinel 'expecting a job resource' (lowercase 'a') is returned and the real converter error is discarded, so the offending field is invisible. It fires before any log streaming starts.","triggerScenarios":"Pressing the logs key on a Job whose stored object cannot convert to the compiled batchv1.Job struct — fields added or retyped by a newer/older apiserver, an aggregated API shadowing jobs.batch, or webhook-patched out-of-schema values. Also hit by embedding code calling TailLogs on such an object.","commonSituations":"Log tailing failing on specific jobs after cluster upgrades while k9s uses older k8s.io/api; jobs mutated by custom controllers with legacy fields; version skew between k9s and the cluster.","solutions":["Inspect the job: kubectl get job.batch <name> -n <ns> -o yaml and fix unexpected or mistyped fields.","Upgrade k9s to a build whose k8s.io/* dependencies match the cluster minor version.","Confirm jobs.batch is served by the core apiserver.","Restart k9s to rebuild informer caches.","If embedding, wrap the converter error with %w to reveal the offending field."],"exampleFix":"// before\nerr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)\nif err != nil {\n    return nil, errors.New(\"expecting a job resource\")\n}\n// after\nu, ok := o.(*unstructured.Unstructured)\nif !ok {\n    return nil, fmt.Errorf(\"expected unstructured job, got %T\", o)\n}\nvar job batchv1.Job\nif err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &job); err != nil {\n    return nil, fmt.Errorf(\"job %q does not match v1 schema: %w\", opts.Path, err)\n}","handlingStrategy":"try-catch","validationCode":"o, err := factory.Get(client.JobGVR, opts.Path, true, labels.Everything())\nif err != nil { return err }\nu, ok := o.(*unstructured.Unstructured)\nif !ok || u.GroupVersionKind().Kind != \"Job\" {\n    return fmt.Errorf(\"not a job: %s\", u.GroupVersionKind())\n}","typeGuard":"func isJob(o runtime.Object) bool {\n    u, ok := o.(*unstructured.Unstructured)\n    return ok && u.GroupVersionKind().Kind == \"Job\" && u.GroupVersionKind().Group == \"batch\"\n}","tryCatchPattern":"var job batchv1.Job\nif err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &job); err != nil {\n    return nil, fmt.Errorf(\"job %q conversion: %w\", opts.Path, err)\n}","preventionTips":["Check log-tail failures against the object's raw YAML before blaming permissions.","Keep k9s and cluster versions aligned; job schemas evolve across releases.","Wrap converter errors with %w in any custom tailing code.","Restart k9s after cluster upgrades to refresh informer caches."],"tags":["kubernetes","go","type-conversion","job","logs"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}