larksuite/cli · error
invalid timestamp %q: %w
Error message
invalid timestamp %q: %w
What it means
sidecar.Verify authenticates HMAC-signed requests: it parses the CanonicalRequest.Timestamp as a Unix-seconds integer before checking drift and the signature. If ParseInt fails it returns 'invalid timestamp %q: %w'. Unlike the CLI helpers, this is a final error from a public API (used by ServeHTTP / verifyWithClientKeys), so callers receive it directly.
Source
Thrown at sidecar/hmac.go:72
c.Identity,
c.AuthHeader,
}, "\n")
}
// Sign computes the HMAC-SHA256 signature over the canonical request string.
func Sign(key []byte, req CanonicalRequest) string {
mac := hmac.New(sha256.New, key)
mac.Write([]byte(req.canonicalString()))
return hex.EncodeToString(mac.Sum(nil))
}
// Verify checks that signature matches the HMAC-SHA256 of the canonical
// request and that the timestamp is within MaxTimestampDrift seconds of now.
// Returns nil on success.
func Verify(key []byte, req CanonicalRequest, signature string) error {
ts, err := strconv.ParseInt(req.Timestamp, 10, 64)
if err != nil {
return fmt.Errorf("invalid timestamp %q: %w", req.Timestamp, err)
}
drift := math.Abs(float64(time.Now().Unix() - ts))
if drift > MaxTimestampDrift {
return fmt.Errorf("timestamp drift %.0fs exceeds limit %ds", drift, MaxTimestampDrift)
}
expected := Sign(key, req)
if !hmac.Equal([]byte(expected), []byte(signature)) {
return fmt.Errorf("HMAC signature mismatch")
}
return nil
}
// Timestamp returns the current Unix epoch seconds as a string.
func Timestamp() string {
return strconv.FormatInt(time.Now().Unix(), 10)
}
View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Send the timestamp header as plain Unix seconds (base-10 integer), e.g. time.Now().Unix() formatted with strconv.Itoa.
- Trim whitespace/quotes from the header value before signing/sending.
- Ensure the header is actually populated — empty string fails ParseInt; check client wiring.
- Recompute the signature over the canonical request with the corrected timestamp so HMAC still matches.
Example fix
// before req.Timestamp = time.Now().Format(time.RFC3339) // "2026-09-04T10:00:00Z" // after req.Timestamp = strconv.FormatInt(time.Now().Unix(), 10) // "1788228000"
Defensive patterns
Strategy: validation
Validate before calling
func validTimestampHeader(v string) bool {
v = strings.TrimSpace(v)
if v == "" { return false }
_, err := strconv.ParseInt(v, 10, 64)
return err == nil
}
// client side: req.Timestamp = strconv.FormatInt(time.Now().Unix(), 10) Type guard
func isUnixSecondsHeader(v string) bool {
_, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
return err == nil
} Try / catch
if err := sidecar.Verify(key, req, sig); err != nil {
var te *sidecar.TimestampError // or string match on 'invalid timestamp'
if strings.Contains(err.Error(), "invalid timestamp") {
http.Error(w, "timestamp must be Unix seconds", http.StatusUnauthorized)
return
}
http.Error(w, "unauthorized", http.StatusUnauthorized)
} Prevention
- Always send the timestamp header as Unix seconds from time.Now().Unix().
- Never format timestamps as RFC3339 or localized text in HMAC canonical requests.
- Trim whitespace/quotes from headers before signing.
- Include the timestamp in signature computation after normalization so Verify's parse and HMAC agree.
When it happens
Trigger: Sending an HMAC-authenticated sidecar request whose X-Timestamp (or equivalent) header is empty, non-numeric ('2026-09-04T10:00:00Z' raw RFC3339, 'now', with quotes/whitespace), or in milliseconds ('1780000000000' overflows nothing but is not seconds — that fails drift instead; the parse failure is for non-integer text).
Common situations: Clients formatting the timestamp as RFC3339 instead of Unix seconds, including surrounding whitespace/quotes, empty header from a misconfigured HTTP client, or clock-sync scripts writing localized digits.
Related errors
- timestamp drift %.0fs exceeds limit %ds
- HMAC signature mismatch
- HTTP %d: [%d] %s
- [%d] %s
- cannot parse %q as ISO 8601
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/4e3e12c2df7e2b08.
Report an issue: GitHub.