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

  1. Quote a duration string with units: `interval: 30s`, `timeout: 1m`.
  2. 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

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


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)