ipfs/kubo · error
unable to parse duration, expected a duration string or a fl
Error message
unable to parse duration, expected a duration string or a float, but got %T
What it means
The Duration config type accepts a JSON string parsed by time.ParseDuration (e.g. "300ms", "1h") or a JSON number interpreted as seconds (float). Any other JSON type (object, array, boolean, null in the default branch) cannot be interpreted and produces this type error.
Source
Thrown at config/types.go:317
func (d *Duration) UnmarshalJSON(b []byte) error {
var v any
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
d.Duration = time.Duration(value)
return nil
case string:
var err error
d.Duration, err = time.ParseDuration(value)
if err != nil {
return err
}
return nil
default:
return fmt.Errorf("unable to parse duration, expected a duration string or a float, but got %T", v)
}
}
var (
_ json.Unmarshaler = (*Duration)(nil)
_ json.Marshaler = (*Duration)(nil)
)
// OptionalInteger represents an integer that has a default value
//
// When encoded in json, Default is encoded as "null".
type OptionalInteger struct {
value *int64
}
// NewOptionalInteger returns an OptionalInteger from a int64.
func NewOptionalInteger(v int64) *OptionalInteger {
return &OptionalInteger{value: &v}View on GitHub (pinned to 329838acdf)
Solutions
- Use a duration string: {"Timeout": "1h30m"}.
- Or use a plain number of seconds: {"Timeout": 3600}.
- Check the producing code so time.Duration-like values are serialized as strings or seconds, not objects.
Example fix
// before
{"Provider.Timeout": {"secs": 60, "nanos": 0}}
// after
{"Provider.Timeout": "60s"} Defensive patterns
Strategy: validation
Validate before calling
func validDuration(v any) error {
switch t := v.(type) {
case string:
_, err := time.ParseDuration(t)
return err
case float64:
return nil // seconds
default:
return fmt.Errorf("duration must be string or seconds number, got %T", v)
}
} Type guard
func isDurationValue(v any) bool {
switch t := v.(type) {
case string:
_, err := time.ParseDuration(t)
return err == nil
case float64:
return true
}
return false
} Try / catch
if err := json.Unmarshal(data, &cfg); err != nil {
if strings.Contains(err.Error(), "unable to parse duration") {
// fix the field to "300ms"-style string or seconds number
}
} Prevention
- Serialize time.Duration as a string (d.String()) or seconds float, never a struct
- Avoid YAML->JSON converters that emit durations as maps
- Unit-test config marshaling round-trips
When it happens
Trigger: Unmarshaling a Duration field with a non-string/non-number JSON value, e.g. {"Timeout": {"secs": 5}} or {"Timeout": true}.
Common situations: Generating config programmatically and emitting a duration as an object (Go time.Duration marshaled as nanosecond integer is fine, but a custom struct is not); hand-editing with a boolean or nested value; YAML-to-JSON conversion emitting a map.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Provide.DHT.OfflineDelay must be non-negative, got %v
- 'true' is not a valid priority
- priority must be positive: %d <= 0
- unable to parse byte size, expected a size string (e.g., "5G
- private networking (swarm.key / LIBP2P_FORCE_PNET) does not
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/e5b1fb5d34596c6d.
Report an issue: GitHub.