services must be a mapping means the services: block of your Compose file is a list: the service name was written with a dash, as in - web:. Remove the dash and indent the service's keys under its name.

A close relative, services.web must be a mapping, means one service was given a plain value, such as web: alpine, instead of a block of keys. Both are fixed by writing the service as a name with its settings indented below it.

The error

PS> docker compose -f compose.yaml config
services must be a mapping

The file:

name: fix10-cy-q2
services:
  - web:
      image: alpine
      command: ["sleep", "1"]

And the one-line service:

name: fix10-cy-q2
services:
  web: alpine
PS> docker compose -f compose.string.yaml config
services.web must be a mapping

Why it happens

services is a mapping from service names to service definitions. With a dash in front, web becomes the key of a list item, so services holds a list and Compose refuses it. In the second file the shape of services is right, but the value of web is the string alpine; a service has to be a mapping of keys such as image and command, so the image name alone is refused.

Unlike the volumes and depends_on messages tested the same day, these two printed no file path and no validating prefix, just the line above, and no line number either. The key path in the message, services or services.web, is the only pointer to where the shape is wrong.

The fix

name: fix10-cy-q2
services:
  web:
    image: alpine
    command: ["sleep", "1"]
PS> docker compose -f compose.fixed.yaml config
name: fix10-cy-q2
services:
  web:
    command:
      - sleep
      - "1"
    image: alpine
...

With web: as a key and its settings indented two spaces under it, both messages go away and config prints the service. Service names are keys, list items are not; dashes belong to values that are lists, such as ports or a service's volumes.

How it was reproduced

Docker Desktop on Windows 11, Docker Compose v5.7.0, engine 29.9.3, Windows PowerShell 5.1. One compose.yaml with the service written as - web: and a second file with web: alpine gave the two messages above on docker compose -f ... config with exit code 1; the corrected file printed the model with exit code 0. No container was started. The project name fix10-cy-q2 is the test's own.

Frequently asked

What does services must be a mapping mean in docker compose?
The services block is a list, because a service name was written with a dash, such as - web:. Remove the dash so each service name is a key.
What does services.web must be a mapping mean?
The service web was given a plain value, such as web: alpine. Write web: on its own line and put image: alpine indented under it.
Why does this compose error show no file name?
On Compose v5.7.0 in the test, both messages printed only the line itself, without the validating prefix and path that the volumes and depends_on messages showed.

More decoded errors in the Fixes category. The same message for the top-level volumes block is covered in volumes must be a mapping.