GoogleContainerTools/skaffold · error

resource found in %s is not a k8s job, verify the manifest p

Error message

resource found in %s is not a k8s job, verify the manifest path is for a job resource

What it means

LoadFromPath decodes the manifest and inspects its group/version/kind; this error is thrown when the resource is valid YAML but is not a batch/v1 Job. The k8sjob package only processes Jobs, so any other kind (Pod, Deployment, CronJob, wrong API version) is rejected with an explicit hint to check the manifest path.

Source

Thrown at pkg/skaffold/k8sjob/util.go:103

	if len(resourceYAML) == 0 {
		return nil, fmt.Errorf("empty file found at path: %s, verify that the manifest path is correct", path)
	}

	// - obj is the API object (e.g., Job)
	// - groupVersionKind is a generic object that allows
	//   detecting the API type we are dealing with, for
	//   accurate type casting later.
	obj, groupVersionKind, err := decoder.Decode(
		[]byte(resourceYAML),
		nil,
		nil)
	if err != nil {
		return nil, err
	}

	// Only process Jobs for now
	if groupVersionKind.Group != "batch" || groupVersionKind.Version != "v1" || groupVersionKind.Kind != "Job" {
		return nil, fmt.Errorf("resource found in %s is not a k8s job, verify the manifest path is for a job resource", path)
	}

	job := obj.(*batchv1.Job)
	if job.Labels == nil {
		job.Labels = map[string]string{}
	}

	return job, nil
}

func GetGenericJob() *batchv1.Job {
	return &batchv1.Job{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Job",
			APIVersion: "batch/v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{},

View on GitHub (pinned to a1189de023)

Solutions

  1. Open the file at the configured path and confirm the first/top-level resource is apiVersion: batch/v1 with kind: Job
  2. Fix the manifest path in the config to point at the actual Job manifest
  3. Update old manifests from batch/v1beta1 to batch/v1 for Job/CronJob kinds
  4. If the file has multiple documents, split them so the Job is the resource loaded

Example fix

// before
apiVersion: batch/v1beta1
kind: CronJob
// after
apiVersion: batch/v1
kind: Job
Defensive patterns

Strategy: validation

Validate before calling

var meta metav1.TypeMeta
if err := yaml.Unmarshal(b, &meta); err != nil {
    return err
}
if meta.GroupVersionKind().Group != "batch" || meta.GroupVersionKind().Kind != "Job" {
    return fmt.Errorf("%s: expected batch/v1 Job, got %s", path, meta.GroupVersionKind())
}

Type guard

func isBatchV1Job(gvk schema.GroupVersionKind) bool {
    return gvk.Group == "batch" && gvk.Version == "v1" && gvk.Kind == "Job"
}

Try / catch

job, err := k8sjob.LoadFromPath(path)
if err != nil {
    if strings.Contains(err.Error(), "not a k8s job") {
        return fmt.Errorf("manifest %q must be a batch/v1 Job", path)
    }
    return err
}

Prevention

When it happens

Trigger: LoadFromPath is given a manifest whose groupVersionKind is not exactly group=batch, version=v1, kind=Job — e.g. the file contains a Pod, a Deployment, a batch/v1beta1 CronJob, or a multi-doc file whose first doc is another kind.

Common situations: Pointing the k8s job logging config at the deployment manifest instead of the job manifest, using an old apiVersion like batch/v1beta1, or a manifest file that starts with a ConfigMap before the Job document.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/7b95d9013d0b9bf2. Report an issue: GitHub.