derailed/k9s · error
expecting cronjob resource
Error message
expecting cronjob resource
What it means
k9s's CronJob DAO fetches a single CronJob from the shared informer cache as *unstructured.Unstructured and converts it into the typed batchv1.CronJob struct with runtime.DefaultUnstructuredConverter.FromUnstructured (internal/dao/cronjob.go:137). The sentinel 'expecting cronjob resource' is returned when that conversion fails, and the underlying converter error is discarded, hiding the real cause. It means the cached object stored under the cronjobs.batch GVR does not fit the k8s.io/api schema the binary was compiled against.
Source
Thrown at internal/dao/cronjob.go:137
FQN: client.FQN(cj.Namespace, cj.Name),
})
}
}
return refs, nil
}
// GetInstance fetch a matching cronjob.
func (c *CronJob) GetInstance(fqn string) (*batchv1.CronJob, error) {
o, err := c.getFactory().Get(c.gvr, fqn, true, labels.Everything())
if err != nil {
return nil, err
}
var cj batchv1.CronJob
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cj)
if err != nil {
return nil, errors.New("expecting cronjob resource")
}
return &cj, nil
}
// ToggleSuspend toggles suspend/resume on a CronJob.
func (c *CronJob) ToggleSuspend(ctx context.Context, path string) error {
ns, n := client.Namespaced(path)
auth, err := c.Client().CanI(ns, c.gvr, n, []string{client.GetVerb, client.UpdateVerb})
if err != nil {
return err
}
if !auth {
return fmt.Errorf("user is not authorized to (un)suspend cronjobs")
}
dial, err := c.Client().Dial()
if err != nil {View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Inspect the offending object for mistyped or out-of-schema fields: kubectl get cronjob.v1.batch <name> -n <ns> -o yaml, then fix or remove them via kubectl edit/patch.
- Check for GVR shadowing: kubectl api-resources --api-group=batch and confirm cronjobs.batch is served by the core apiserver, not an aggregated apiservice.
- Upgrade (or rebuild) k9s so its k8s.io/api, apimachinery and client-go versions match the cluster minor version.
- Restart k9s to force informer caches to re-list objects after an upgrade or webhook change.
- If you embed this DAO, wrap the converter error with fmt.Errorf and %w to surface the offending field instead of the generic sentinel.
Example fix
// before
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cj)
if err != nil {
return nil, errors.New("expecting cronjob resource")
}
// after
u, ok := o.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("expected unstructured cronjob, got %T", o)
}
var cj batchv1.CronJob
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &cj); err != nil {
return nil, fmt.Errorf("cronjob %q does not match v1 schema: %w", fqn, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
o, err := factory.Get(client.CronJobGVR, fqn, true, labels.Everything())
if err != nil { return err }
u, ok := o.(*unstructured.Unstructured)
if !ok || u.GroupVersionKind().Kind != "CronJob" {
return fmt.Errorf("not a cronjob: %s", u.GroupVersionKind())
} Type guard
func isCronJob(o runtime.Object) bool {
u, ok := o.(*unstructured.Unstructured)
return ok && u.GroupVersionKind().Kind == "CronJob" && u.GroupVersionKind().Group == "batch"
} Try / catch
var cj batchv1.CronJob
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &cj); err != nil {
return nil, fmt.Errorf("cronjob %q conversion: %w", fqn, err) // keep root cause, name the object
} Prevention
- Keep k9s and its vendored k8s.io/* libraries within one minor version of the cluster.
- Apply schema-valid manifests; avoid raw patches that can inject wrong field types.
- In custom DAO code, type-assert to *unstructured.Unstructured before conversion and wrap errors with %w.
- After cluster upgrades, restart k9s so informer caches re-list every object.
When it happens
Trigger: Calling dao.CronJob.GetInstance(fqn) — used by the CronJob detail view, ToggleSuspend and job triggering — when the object carries a field whose JSON type cannot be assigned to the compiled batchv1.CronJob struct: fields added or retyped by a newer/older apiserver, an aggregated API or CRD shadowing cronjobs.batch, or a webhook-patched object with out-of-schema values.
Common situations: Running an old k9s build (stale vendored k8s.io/* libraries) against a much newer cluster or vice versa; clusters with custom mutating webhooks that write non-schema-conformant fields; reading an object right after a cluster upgrade while the informer cache still holds the old shape; embedding k9s DAOs in another tool with a custom factory.
Related errors
- expecting Deployment resource
- expecting DaemonSet resource
- expecting Job resource
- no accessor for %q
- expecting ServiceAccount resource
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/4eede3bd75defa6d.
Report an issue: GitHub.