GoogleContainerTools/skaffold · error

DEPLOY_CLOUD_RUN_UPDATE_SERVICE_ERR

DEPLOY_CLOUD_RUN_UPDATE_SERVICE_ERR

Error message

error deploying Cloud Run Service: %s

What it means

After determining the service exists or not, `deployService` performs the actual write: `Services.Create` for a new service or `Services.ReplaceService` for an existing one. Any error from either call's `.Do()` is wrapped as 'error deploying Cloud Run Service: %s' with code DEPLOY_CLOUD_RUN_UPDATE_SERVICE_ERR.

Source

Thrown at pkg/skaffold/deploy/cloudrun/deploy.go:307

	_, err := getCall.Do()

	if err != nil {
		gErr, ok := err.(*googleapi.Error)
		if !ok || gErr.Code != http.StatusNotFound {
			return nil, sErrors.NewError(fmt.Errorf("error checking Cloud Run State: %w", err), &proto.ActionableErr{
				Message: err.Error(),
				ErrCode: proto.StatusCode_DEPLOY_CLOUD_RUN_GET_SERVICE_ERR,
			})
		}
		// This is a new service, we need to create it
		createCall := crclient.Projects.Locations.Services.Create(parent, service)
		_, err = createCall.Do()
	} else {
		replaceCall := crclient.Projects.Locations.Services.ReplaceService(sName, service)
		_, err = replaceCall.Do()
	}
	if err != nil {
		return nil, sErrors.NewError(fmt.Errorf("error deploying Cloud Run Service: %s", err), &proto.ActionableErr{
			Message: err.Error(),
			ErrCode: proto.StatusCode_DEPLOY_CLOUD_RUN_UPDATE_SERVICE_ERR,
		})
	}
	return &resName, nil
}

func (d *Deployer) forceSendValueOfMaxRetries(job *run.Job, manifest []byte) {
	maxRetriesPath := []string{"spec", "template", "spec", "template", "spec"}
	node := make(map[string]interface{})

	if err := k8syaml.Unmarshal(manifest, &node); err != nil {
		logger.Entry(context.TODO()).Debugf("Error unmarshaling job into map, skipping maxRetries ForceSendFields logic: %v", err)
		return
	}

	for _, field := range maxRetriesPath {
		value := node[field]

View on GitHub (pinned to a1189de023)

Solutions

  1. Read the API message in the error (it's surfaced via `Message: err.Error()`) — Cloud Run returns detailed reasons for rejected specs
  2. Ensure the image is pushed and accessible: `gcloud run deploy` equivalent requires the container image to exist; check `--default-repo` substitution and the `spec.template.spec.containers[0].image` value
  3. Grant the Cloud Run service agent access to pull from the image registry (Artifact Registry Reader role) for cross-project images
  4. Validate the service spec against the Cloud Run schema; common rejects are bad health-check probes, missing port config, or invalid resource limits
  5. Retry if the failure was transient (e.g. 429/503); check quotas in the GCP console

Example fix

# before: image not found
image: my-app:latest   # never pushed
# after
skaffold deploy --default-repo=gcr.io/my-project
# renders image: gcr.io/my-project/my-app:latest
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the image exists and is pullable before deploying
func imageExists(image string) error {
	name := strings.SplitN(image, ":", 2)[0]
	return exec.Command("gcloud", "artifacts", "docker", "images", "describe", name).Run()
}

Try / catch

if err := deployer.Deploy(ctx, out, artifacts); err != nil {
	var sErr *sErrors.Error
	if errors.As(err, &sErr) && sErr.Status().ErrCode == proto.StatusCode_DEPLOY_CLOUD_RUN_UPDATE_SERVICE_ERR {
		msg := sErr.Status().Message
		switch {
		case strings.Contains(msg, "IMAGE_PULL") || strings.Contains(msg, "image"):
			return fmt.Errorf("check image exists and is pullable by Cloud Run (%s)", msg)
		case strings.Contains(msg, "429") || strings.Contains(msg, "503"):
			time.Sleep(10 * time.Second)
			return deployer.Deploy(ctx, out, artifacts) // retry transient failures
		}
	}
	return err
}

Prevention

When it happens

Trigger: `createCall.Do()` or `replaceCall.Do()` returns an error in `deployService` — e.g. image not found/pull denied, invalid service spec rejected by the API (400), revision creation failure, quota exceeded, or conditional update conflicts.

Common situations: The image referenced in the manifest isn't pushed to a registry the project can pull from (private repo, wrong tag); `--default-repo` substitution missing so the API can't resolve the image; spec violations like missing `containers` or invalid probes; regional quota limits; IAM preventing the run service agent from pulling the image.

Related errors


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