docker/cli · error
invalid type %T for duration
Error message
invalid type %T for duration
What it means
Thrown by transformStringToDuration for duration fields (e.g. `healthcheck.interval`, `restart_policy.delay`, deploy update/rollback timings). The value MUST be a string parseable by time.ParseDuration; ints, bools, or maps are rejected. (Note: a string that fails to parse returns a separate time.ParseDuration error, not this one.)
Solutions
- Quote a duration string with units: `interval: 30s`, `timeout: 1m`.
- Ensure env interpolation yields a string with units, not a bare integer.
Example fix
# before
web:
healthcheck:
interval: 30
# after
web:
healthcheck:
interval: 30s Defensive patterns
Strategy: type-guard
Validate before calling
func validateDuration(field string, v any) error {
s, ok := v.(string)
if !ok {
return fmt.Errorf("%s must be a duration string, got %T", field, v)
}
if _, err := time.ParseDuration(s); err != nil {
return fmt.Errorf("%s: %w", field, err)
}
return nil
} Type guard
func isDurationString(v any) bool {
s, ok := v.(string)
if !ok {
return false
}
_, err := time.ParseDuration(s)
return err == nil
} Prevention
- Always include units for durations (30s, 1m, 2h).
- Quote values produced by env interpolation to keep them strings.
- Run `docker compose config` to surface parse errors early.
When it happens
Trigger: A duration field is set to a bare number (`interval: 30`) or a map instead of a quoted duration string.
Common situations: Writing `interval: 30` instead of `interval: 30s`; YAML treating an unquoted value as a non-string type.
Related errors
- invalid type %T for healthcheck.test
- invalid type %T for ulimits
- invalid type %T for map[string]string
- invalid type %T for port
- invalid type %T for secret
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/f9f7a4073d11c984.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/loader/loader.go:915
switch value := value.(type) {
case int:
return int64(value), nil
case string:
return units.RAMInBytes(value)
}
panic(fmt.Errorf("invalid type for size %T", value))
}
var transformStringToDuration TransformerFunc = func(value any) (any, error) {
switch value := value.(type) {
case string:
d, err := time.ParseDuration(value)
if err != nil {
return value, err
}
return types.Duration(d), nil
default:
return value, fmt.Errorf("invalid type %T for duration", value)
}
}
func toServicePortConfigs(value string) ([]any, error) {
// short syntax ([ip:]public:private[/proto])
//
// TODO(thaJeztah): we need an equivalent that handles the "ip-address" part without depending on the nat package.
ports, portBindings, err := nat.ParsePortSpecs([]string{value})
if err != nil {
return nil, err
}
// We need to sort the key of the ports to make sure it is consistent
keys := make([]string, 0, len(ports))
for port := range ports {
keys = append(keys, string(port))
}
sort.Strings(keys)
View on GitHub (pinned to 4f84911bfe)