docker/cli · error

invalid type %T for external

Error message

invalid type %T for external

What it means

Thrown by transformExternal, the transformer for the `external` field on networks/volumes/secrets/configs. It accepts only a boolean (`external: true`) or a mapping (`external: { name: x }`); any other type (string, list, number) is rejected.

Solutions

  1. Use a bare boolean: `external: true`.
  2. Or use the map form: `external: { name: my-resource }`.

Example fix

# before
volumes:
  data:
    external: "true"
# after
volumes:
  data:
    external: true
Defensive patterns

Strategy: type-guard

Validate before calling

func validateExternal(v any) error {
    switch v.(type) {
    case bool, map[string]any:
        return nil
    default:
        return fmt.Errorf("external must be bool or map, got %T", v)
    }
}

Type guard

func isValidExternal(v any) bool {
    switch v.(type) {
    case bool:
        return true
    case map[string]any:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: A resource sets `external: "true"` (string), `external: [true]`, or `external: yes-maybe`. The transformer runs during Transform() of the resource block.

Common situations: Quoting `true`, using a placeholder/template substitution that yields a non-bool, or env interpolation that produces a string.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/3e337909c01153e2. Report an issue: GitHub.

Appendix: source

Thrown at cli/compose/loader/loader.go:703

var transformMapStringString TransformerFunc = func(data any) (any, error) {
	switch value := data.(type) {
	case map[string]any:
		return toMapStringString(value, false), nil
	case map[string]string:
		return value, nil
	default:
		return data, fmt.Errorf("invalid type %T for map[string]string", value)
	}
}

var transformExternal TransformerFunc = func(data any) (any, error) {
	switch value := data.(type) {
	case bool:
		return map[string]any{"external": value}, nil
	case map[string]any:
		return map[string]any{"external": true, "name": value["name"]}, nil
	default:
		return data, fmt.Errorf("invalid type %T for external", value)
	}
}

var transformServicePort TransformerFunc = func(data any) (any, error) {
	switch entries := data.(type) {
	case []any:
		// We process the list instead of individual items here.
		// The reason is that one entry might be mapped to multiple ServicePortConfig.
		// Therefore we take an input of a list and return an output of a list.
		ports := []any{}
		for _, entry := range entries {
			switch value := entry.(type) {
			case int:
				v, err := toServicePortConfigs(strconv.Itoa(value))
				if err != nil {
					return data, err
				}
				ports = append(ports, v...)

View on GitHub (pinned to 4f84911bfe)