docker/compose · error

can't watch service %q with action %s without a build contex

Error message

can't watch service %q with action %s without a build context

What it means

While collecting watch rules, compose validates each trigger: a rebuild action requires the service to be built from source, because rebuild re-runs the build. If the service has no build: section (image-only), watch cannot rebuild it and fails naming the service and action.

Source

Thrown at pkg/compose/watch.go:221

	)
	for serviceName, service := range project.Services {
		config, err := loadDevelopmentConfig(service, project)
		if err != nil {
			return nil, err
		}

		if service.Develop != nil {
			config = service.Develop
		}

		if config == nil {
			continue
		}

		for _, trigger := range config.Watch {
			if trigger.Action == types.WatchActionRebuild {
				if service.Build == nil {
					return nil, fmt.Errorf("can't watch service %q with action %s without a build context", service.Name, types.WatchActionRebuild)
				}
				if options.Build == nil {
					return nil, fmt.Errorf("--no-build is incompatible with watch action %s in service %s", types.WatchActionRebuild, service.Name)
				}
				// set the service to always be built - watch triggers `Up()` when it receives a rebuild event
				service.PullPolicy = types.PullPolicyBuild
				project.Services[serviceName] = service
			}
		}

		for _, trigger := range config.Watch {
			if isSync(trigger) && checkIfPathAlreadyBindMounted(trigger.Path, service.Volumes) {
				logrus.Warnf("path '%s' also declared by a bind mount volume, this path won't be monitored!\n", trigger.Path)
				continue
			} else {
				shouldInitialSync := trigger.InitialSync

				// Check legacy extension attribute for backward compatibility

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Remove the rebuild watch trigger from the image-only service, or change its action to sync or sync+restart.
  2. Add a build: section (context/dockerfile) to the service if rebuild-on-change is intended.
  3. Split long-lived source watching into a separate build-based compose service.

Example fix

# before
services:
  api:
    image: registry.example.com/team/api:1.0   # no build:
    develop:
      watch:
        - action: rebuild
          path: ./src

# after
services:
  api:
    image: registry.example.com/team/api:1.0
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
Defensive patterns

Strategy: validation

Validate before calling

func rebuildTriggersBackedByBuild(project *types.Project) error {
	for name, svc := range project.Services {
		if svc.Develop == nil { continue }
		for _, t := range svc.Develop.Watch {
			if t.Action == types.WatchActionRebuild && svc.Build == nil {
				return fmt.Errorf("service %q has rebuild watch but no build section", name)
			}
		}
	}
	return nil
}

Try / catch

if _, err := composeService.Watch(ctx, projectName, opts); err != nil {
    if strings.Contains(err.Error(), "without a build context") {
        // change the trigger action to sync/sync+restart or add build: to the service
    }
    return err
}

Prevention

When it happens

Trigger: Configuring develop.watch with action: rebuild on a service that specifies only image: (no build context). Starting watch then fails during rule construction.

Common situations: Copy-pasting a watch block from a build-based service onto an image-based one; switching a service from build to a prebuilt image but leaving its develop.watch rebuild rule in place.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/baee40cf7e6d12f0. Report an issue: GitHub.