t8y2/dbx · error
ttl must be a positive integer
Error message
ttl must be a positive integer
What it means
Validation error thrown by put at kv.go:267 when the ttl parameter is provided but is zero or negative. etcd's Lease.Grant requires a TTL of at least 1 second; the driver checks this before calling Grant so an obviously invalid ttl fails fast instead of producing a raw etcd gRPC error.
Source
Thrown at agents/drivers/etcd-go/kv.go:267
if (hasLease && hasTtl) || (preserveLease && (hasLease || hasTtl)) {
return nil, errors.New("lease, ttl, and preserveLease cannot be specified together")
}
ctx, cancel := s.beginOperation()
defer s.endOperation(cancel)
if preserveLease {
revision, err := putPreservingLease(client, ctx, key, value)
if err != nil {
return nil, err
}
return map[string]any{"revision": longString(revision)}, nil
}
var leaseID clientv3.LeaseID
var grantedLeaseID clientv3.LeaseID
if hasTtl {
if *ttlValue <= 0 {
return nil, errors.New("ttl must be a positive integer")
}
grant, err := client.Lease.Grant(ctx, *ttlValue)
if err != nil {
return nil, err
}
grantedLeaseID = grant.ID
leaseID = grant.ID
} else if hasLease {
leaseID = clientv3.LeaseID(*leaseValue)
}
revision, err := func() (int64, error) {
if expectedModRevision != nil || expectedCreateRevision != nil {
var comparisons []clientv3.Cmp
if expectedModRevision != nil {
comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(key), "=", *expectedModRevision))
}
if expectedCreateRevision != nil {View on GitHub (pinned to c0390bff16)
Solutions
- Ensure ttl is a positive integer (seconds, minimum 1) before calling put.
- Treat ttl <= 0 as 'no lease': omit the parameter entirely instead of sending 0.
- Clamp or guard in caller code: ttl > 0 ? { ttl } : {}.
- Fix the upstream calculation (e.g. Math.max(1, expirySeconds - now)) if TTL is derived.
Example fix
// before
put(session, key, value, { ttl: ttlFromConfig }) // ttlFromConfig = 0
// after
const opts = ttlFromConfig > 0 ? { ttl: ttlFromConfig } : {};
put(session, key, value, opts) Defensive patterns
Strategy: validation
Validate before calling
if (opts.ttl != null && (!Number.isInteger(opts.ttl) || opts.ttl <= 0)) {
throw new Error('ttl must be a positive integer (seconds)');
} Type guard
function isValidTtl(ttl) { return ttl != null && Number.isInteger(ttl) && ttl > 0; } Try / catch
try { return put(session, key, value, opts); }
catch (e) {
if (String(e).includes('ttl must be a positive integer'))
return put(session, key, value, { ...opts, ttl: Math.max(1, opts.ttl) });
throw e;
} Prevention
- Treat ttl <= 0 as 'no lease' and omit the field, don't send 0.
- Type-check ttl at config-load time (positive integer).
- Guard derived TTLs: Math.max(1, expirySeconds - nowSeconds).
- Document that ttl is in whole seconds, minimum 1.
When it happens
Trigger: Calling put with ttl: 0, ttl: -1, or any ttl <= 0 — commonly the result of an unset/zero-valued numeric variable being passed through as the ttl parameter.
Common situations: Defaults where ttl defaults to 0 meaning 'no ttl' but is passed unconditionally; computing TTL from a difference (expiry - now) that lands at 0 or negative; deserializing configs where ttl is '0'/'-1' as a sentinel for disabled.
Related errors
- lease, ttl, and preserveLease cannot be specified together
- id must be a positive integer or 0
- ttl must be a positive integer
- ETCD_INVALID_%s
- user is required
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/c77928672a50b33b.
Report an issue: GitHub.