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
- Use a bare boolean: `external: true`.
- 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
- Keep `external` as a bare boolean or a `{ name: ... }` map.
- Beware env interpolation that turns `external` into a string.
- Lint configs before deploy.
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
- network : network.external.name and network.name conflict…
- conflicting parameters "external" and
- volume : volume.external.name and volume.name conflict…
- invalid type %T for service volume
- network is declared as external, but could not be found…
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)