docker/compose · error

failed to scan secret file %s: %w

Error message

failed to scan secret file %s: %w

What it means

The pre-publish secret scan failed reading a top-level secret defined by file (secrets.<name>.file). Compose attempts to scan secret file contents like env and config files; a read failure aborts the publish.

Source

Thrown at pkg/compose/publish.go:739

	}

	// Check configs defined by files
	for _, config := range project.Configs {
		if config.File != "" {
			findings, err := scan.ScanFile(config.File)
			if err != nil {
				return nil, fmt.Errorf("failed to scan config file %s: %w", config.File, err)
			}
			allFindings = append(allFindings, findings...)
		}
	}

	// Check secrets defined by files
	for _, secret := range project.Secrets {
		if secret.File != "" {
			findings, err := scan.ScanFile(secret.File)
			if err != nil {
				return nil, fmt.Errorf("failed to scan secret file %s: %w", secret.File, err)
			}
			allFindings = append(allFindings, findings...)
		}
	}

	return allFindings, nil
}

func composeFileAsByteReader(ctx context.Context, filePath string, project *types.Project) (io.Reader, error) {
	base, err := loadUnresolvedFile(ctx, project, filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to load compose file %s: %w", filePath, err)
	}
	in, err := base.MarshalYAML()
	if err != nil {
		return nil, err
	}
	return bytes.NewBuffer(in), nil

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Ensure the secret file exists at the referenced path and is readable by the user running compose publish.
  2. Add a CI step that materializes secret files (from the CI secret store) before publishing.
  3. Correct the path or use environment-based secrets (environment: key) instead of file-based ones when the file is not available at publish time.
  4. If secrets are external (driver-based), remove the file: reference so no local scan is required.

Example fix

# before
secrets:
  db-password:
    file: /run/secrets/db_password   # absent at publish time

# after
secrets:
  db-password:
    environment: DB_PASSWORD         # resolved from project environment
Defensive patterns

Strategy: validation

Validate before calling

func secretFilesReadable(project *types.Project) error {
	for name, sec := range project.Secrets {
		if sec.File == "" {
			continue
		}
		if fi, err := os.Stat(sec.File); err != nil || !fi.Mode().IsRegular() {
			return fmt.Errorf("secret %s file %q unreadable", name, sec.File)
		}
	}
	return nil
}

Try / catch

if err := publish(ctx, opts); err != nil {
    if strings.Contains(err.Error(), "failed to scan secret file") {
        // inject the secret file from the secret store, then retry
    }
    return err
}

Prevention

When it happens

Trigger: Publishing a project with a secrets: section whose file path cannot be read: missing-but-symlinked, directory instead of file, permission denied at read time, or I/O error.

Common situations: Secret files injected only at runtime (not present when publishing); paths pointing outside the repository; files owned by root in CI; rotated credentials files replaced mid-scan.

Related errors


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