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

  1. Send the timestamp header as plain Unix seconds (base-10 integer), e.g. time.Now().Unix() formatted with strconv.Itoa.
  2. Trim whitespace/quotes from the header value before signing/sending.
  3. Ensure the header is actually populated — empty string fails ParseInt; check client wiring.
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/4e3e12c2df7e2b08. Report an issue: GitHub.