docker/compose · error

unsupported OCI version: %s

Error message

unsupported OCI version: %s

What it means

When pushing a compose project as an OCI artifact, push.go builds a manifest according to the requested OCI compat version and only knows how to lay out 1.0 (config layer with project bytes, empty artifactType) and 1.1 (empty-JSON config descriptor plus ComposeProjectArtifactType). Any other value in the version variable hits the default arm and is rejected before any network I/O.

Source

Thrown at internal/oci/push.go:194

		// tooling falls back to the config media type, so this is used to
		// indicate that it's not a container image but custom content.
		configData := []byte("{}")
		config = v1.Descriptor{
			MediaType: ComposeEmptyConfigMediaType,
			Digest:    digest.FromBytes(configData),
			Size:      int64(len(configData)),
			Data:      configData,
		}
		// N.B. OCI 1.0 does NOT support specifying the artifact type, so it's
		//		left as an empty string to omit it from the marshaled JSON
		artifactType = ""
		toPush = append(toPush, config)
	case api.OCIVersion1_1:
		config = v1.DescriptorEmptyJSON
		artifactType = ComposeProjectArtifactType
		toPush = append(toPush, config)
	default:
		return v1.Descriptor{}, nil, fmt.Errorf("unsupported OCI version: %s", ociCompat)
	}

	manifest, err := json.Marshal(v1.Manifest{
		Versioned:    specs.Versioned{SchemaVersion: 2},
		MediaType:    v1.MediaTypeImageManifest,
		ArtifactType: artifactType,
		Config:       config,
		Layers:       layers,
		Annotations: map[string]string{
			"org.opencontainers.image.created": time.Now().Format(time.RFC3339),
		},
	})
	if err != nil {
		return v1.Descriptor{}, nil, err
	}

	manifestDescriptor := v1.Descriptor{
		MediaType: v1.MediaTypeImageManifest,

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Use one of the supported constants (api.OCIVersion1_1 for modern registries, api.OCIVersion1_0 for strict 1.0 registries).
  2. If you introduced a new version constant, add a case to the switch in push.go mapping it to the right config/artifactType layout.
  3. Validate the version at the CLI/config boundary so unhandled values fail with a clear message before push starts.
  4. Check which values your compose build supports: grep for OCIVersion in internal/api.

Example fix

// before (caller)
ociCompat := "1.2" // unhandled by push.go

// after (caller)
ociCompat := api.OCIVersion1_1 // or api.OCIVersion1_0
Defensive patterns

Strategy: validation

Validate before calling

switch ociCompat {
case api.OCIVersion1_0, api.OCIVersion1_1:
default:
    return fmt.Errorf("unsupported OCI version %q; use %s or %s", ociCompat, api.OCIVersion1_0, api.OCIVersion1_1)
}

Type guard

func supportedOCIVersion(v api.OCIVersion) bool {
    return v == api.OCIVersion1_0 || v == api.OCIVersion1_1
}

Prevention

When it happens

Trigger: Calling the artifact push path with an ociCompat value other than api.OCIVersion1_0/OCIVersion1_1 — typically a new constant added to the API package without extending this switch, or a caller-supplied string parsed into an unhandled value (e.g. via a future --oci-compat variant flag).

Common situations: Version skew after adding a new OCI version constant to internal/api without updating push.go; tests iterating all api.OCIVersion* constants; hand-rolled callers passing a raw version string instead of the defined constants.

Related errors


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