{"record":{"id":"dda11988de66d372","repo":"derailed/k9s","slug":"expecting-job-resource","errorCode":null,"errorMessage":"expecting Job resource","messagePattern":"expecting Job resource","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/job.go","lineNumber":58,"sourceCode":"\n\treturn render.ExtractImages(&job.Spec.Template.Spec), nil\n}\n\n// List returns a collection of resources.\nfunc (j *Job) List(ctx context.Context, ns string) ([]runtime.Object, error) {\n\too, err := j.Resource.List(ctx, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tctrl, _ := ctx.Value(internal.KeyPath).(string)\n\t_, n := client.Namespaced(ctrl)\n\n\tll := make([]runtime.Object, 0, 10)\n\tfor _, o := range oo {\n\t\tvar j batchv1.Job\n\t\terr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &j)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"expecting Job resource\")\n\t\t}\n\t\tif n == \"\" {\n\t\t\tll = append(ll, o)\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, r := range j.OwnerReferences {\n\t\t\tif r.Name == n {\n\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) {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/job.go#L40-L76","documentation":"Job.List fetches all Jobs in a namespace via the generic resource list, converts each *unstructured.Unstructured into batchv1.Job, and filters by owner reference against the controller path carried in ctx (internal.KeyPath) for the CronJob detail view (internal/dao/job.go:58). If any listed Job fails conversion, the sentinel 'expecting Job resource' aborts the whole listing and the underlying converter error is dropped.","triggerScenarios":"Browsing jobs (or opening a CronJob's owned-jobs list) in a namespace where at least one stored Job object cannot convert to the compiled batchv1.Job struct — k9s/cluster version skew, an aggregated API shadowing jobs.batch, or jobs patched with out-of-schema fields. A single bad Job breaks the entire job browser for that namespace.","commonSituations":"k9s builds with older k8s.io/api against newer clusters where job spec/status fields changed; namespaces with Jobs created by legacy CRD controllers; GitOps force-applied legacy job manifests.","solutions":["Audit the namespace: kubectl get jobs.batch -n <ns> -o yaml and look for unexpected or mistyped fields; fix offending manifests.","Upgrade k9s so its vendored k8s.io/* versions match the cluster.","Verify jobs.batch is served by the core apiserver and not shadowed.","Restart k9s to rebuild informer caches.","Patch the loop to skip and log unparsable Jobs instead of failing the list."],"exampleFix":"// before\nvar j batchv1.Job\nerr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &j)\nif err != nil {\n    return nil, errors.New(\"expecting Job resource\")\n}\n// after\nu, ok := o.(*unstructured.Unstructured)\nif !ok {\n    continue\n}\nvar j batchv1.Job\nif err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &j); err != nil {\n    slog.Warn(\"skip unparsable job\", \"fqn\", client.FQN(u.GetNamespace(), u.GetName()), \"err\", err)\n    continue\n}","handlingStrategy":"try-catch","validationCode":"for _, o := range oo {\n    u, ok := o.(*unstructured.Unstructured)\n    if !ok || u.GroupVersionKind().Kind != \"Job\" {\n        continue\n    }\n    // safe to convert below\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":"if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &j); err != nil {\n    slog.Warn(\"skip unparsable job\", \"fqn\", fqn, \"err\", err)\n    continue // listing must survive one bad object\n}","preventionTips":["Design list paths to skip unparsable objects and log them, never abort the browser.","Wrap conversion errors with %w including the object name.","Clean up legacy Jobs mutated by old controllers when upgrading clusters.","Keep k9s's k8s.io/api in sync with the cluster."],"tags":["kubernetes","go","type-conversion","job","dao"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}