docker/compose · error

failed to load compose file %s: %w

Error message

failed to load compose file %s: %w

What it means

Thrown during the pre-publish scan for sensitive data when one of the compose files in the extends/include graph cannot be re-loaded in unresolved form. The scanner walks a BFS queue starting from project.ComposeFiles and follows local extends parents; a failure to load any file in that chain aborts the publish.

Source

Thrown at pkg/compose/publish.go:490

// artifact.
func collectEnvCheckFindings(ctx context.Context, project *types.Project) (*envCheckFindings, error) {
	findings := &envCheckFindings{services: map[string]*serviceEnvFindings{}}
	literalCfgs := map[string]struct{}{}
	keywordDetector := keyword.NewDetector("0")

	seen := map[string]struct{}{}
	queue := slices.Clone(project.ComposeFiles)
	for len(queue) > 0 {
		file := queue[0]
		queue = queue[1:]
		if _, ok := seen[file]; ok {
			continue
		}
		seen[file] = struct{}{}

		unresolved, err := loadUnresolvedFile(ctx, project, file)
		if err != nil {
			return nil, fmt.Errorf("failed to load compose file %s: %w", file, err)
		}

		for _, service := range unresolved.Services {
			recordServiceEnvFindings(findings.services, keywordDetector, service)
			if parent := localExtendsParent(service); parent != "" {
				queue = append(queue, parent)
			}
		}
		for name, config := range unresolved.Configs {
			// config.Environment is a variable *name* (only the name is
			// published, not its resolved value) so it is not a leak. Inline
			// config.Content is what ends up in the artifact. compose-go
			// enforces that file, environment, and content are mutually
			// exclusive. The map key is the name as written in the compose
			// file; config.Name is the project-namespaced version, which is
			// less helpful when surfaced to the user.
			if config.Content != "" && configContentLooksLiteral(config.Content, keywordDetector) {
				literalCfgs[name] = struct{}{}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Validate every file in the extends chain resolves: run docker compose config to make the loader surface the exact failing file.
  2. Fix the reported file: correct YAML syntax errors, restore the missing base file, or repair its path in the extends section.
  3. Ensure the process can read each file (permissions) and that relative extends paths are resolved against the correct working directory.
  4. Remove or inline the extends reference if the base file is no longer meant to be part of the published artifact.

Example fix

# before
services:
  web:
    extends:
      file: ./base/deleted-base.yml
      service: web-base

# after
services:
  web:
    extends:
      file: ./base/base.yml   # existing, valid YAML
      service: web-base
Defensive patterns

Strategy: validation

Validate before calling

// before publishing, dry-run the whole extends graph
if _, err := loader.Load(ctx, loader.LoadConfig{ConfigPaths: project.ComposeFiles}); err != nil {
    return fmt.Errorf("compose files not loadable, aborting publish: %w", err)
}

Try / catch

if err := scanAndPublish(ctx, project); err != nil {
    if errors.Is(err, os.ErrPermission) || strings.Contains(err.Error(), "failed to load compose file") {
        // re-validate file set, fix or restore the file, re-run
    }
    return err
}

Prevention

When it happens

Trigger: Publishing a project where a base file referenced via extends (or the top-level compose file itself) fails to load through loadUnresolvedFile: invalid YAML, missing file, unreadable file, or unsupported syntax that the loader rejects.

Common situations: A base compose file was deleted or renamed after the project was initially loaded; extends points to a file with a syntax error; file permissions changed between the initial load and the publish scan; includes pulling a remote file that no longer resolves.

Related errors


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