service "worker" has neither an image nor a build context specified: invalid compose project means that service in your Compose file has no image: and no build: key, so Compose has nothing to create its container from. Add one of the two to the named service.

Use image: to run an existing image, or build: pointing at the folder that holds the service's Dockerfile. If you think the key is there, check that it sits under the service the message names.

The error

PS> docker compose config --quiet
service "worker" has neither an image nor a build context specified: invalid compose project

The file that produced it:

name: fix8-cli-c6
services:
  web:
    image: alpine
    command: ["sleep", "1"]
  worker:
    command: ["echo", "work"]
    depends_on:
      - web

Why it happens

Every container starts from an image. A Compose service says where that image comes from in one of two ways: image: names one to pull or use from the local cache, and build: tells Compose to build one from a folder. The file itself is valid YAML; the missing image is a problem of the project as a whole, which can be built from several files. In the test, a second file holding only services: worker: image: alpine made the same broken file valid:

PS> docker compose -f compose.yaml -f compose.override-image.yaml config --quiet

That command printed nothing and exited with 0. So the message can also mean that an override file meant to supply the image was not passed. A service copied from another file without its image: line, or with build: commented out, gives it too.

The fix

name: fix8-cli-c6
services:
  web:
    image: alpine
    command: ["sleep", "1"]
  worker:
    image: alpine
    command: ["echo", "work"]
    depends_on:
      - web
PS> docker compose -f compose.fixed.yaml config --quiet
PS> docker compose -f compose.fixed.yaml up
worker-1  | work

With image: alpine on worker, the project validates, both containers start and the worker prints its line (the up output is trimmed to that line). For a service built from your own code, write build: ./worker (a folder with a Dockerfile). In a second test with both image: fix8-cli-img:6 and build: ./worker, Compose first tried to pull the name, printed pull access denied for fix8-cli-img, then built the folder; afterwards docker images fix8-cli-img:6 listed the built image under that name.

How it was reproduced

Docker Desktop on Windows 11, engine 29.9.2, Docker Compose v5.6.1, Windows PowerShell 5.1, the cached alpine:latest image. A two-service compose.yaml whose second service had only command and depends_on gave the message above on both config and up; adding image: alpine fixed it. The project name fix8-cli-c6 is the test's own.

Frequently asked

What does has neither an image nor a build context specified mean?
The named service in your Compose file has no image key and no build key, so Compose cannot tell what to start. Add image: with an image name or build: with the folder that holds its Dockerfile.
Can a docker compose service have both image and build?
Yes. In this test Compose v5.6.1 first tried to pull the image name, failed because no such repository existed, then built the build folder and tagged the result with the name given in image.
Why does docker compose say invalid compose project and not the line number?
The file passes the schema check. The missing image is found later, when Compose assembles the project from all its files, so there is no single line to point at.

More decoded errors in the Fixes category. When the missing piece is a service name in depends_on, see depends on undefined service.