JuliusBrussee/caveman · warning

invalid --since %q (want RFC3339): %w

Error message

invalid --since %q (want RFC3339): %w

What it means

Returned by Store.ObserveSummarySince when the optional --since value is non-empty but does not parse as RFC3339. The store timestamps rows in UTC using RFC3339-derived formatting, so the cutoff must be an exact RFC3339 timestamp like 2026-01-02T15:04:05Z or with an explicit offset (2026-01-02T10:04:05-05:00).

Source

Thrown at proxy/internal/store/store.go:741

	out.Basis = "inferred"
	return out, nil
}

// ObserveSummarySince returns the compact observe-estimate object, optionally
// filtered to rows at or after an RFC3339 instant (the wrap session start). It
// mixes the observe-only would-have-saved numbers with the real compression cut so
// one call serves both the "would have cut" (observe) and "cut" (compress) session
// lines. Everything it reports is `inferred`; savings_usd is whatever was truly
// booked (0 in observe mode), and would_save_usd stays nil unless a priced row
// produced one.
func (s *Store) ObserveSummarySince(since string) (ObserveSummary, error) {
	out := ObserveSummary{Basis: "inferred", TokenAccounting: map[string]int64{}}
	where := ""
	var args []any
	if trimmed := strings.TrimSpace(since); trimmed != "" {
		t, err := time.Parse(time.RFC3339, trimmed)
		if err != nil {
			return out, fmt.Errorf("invalid --since %q (want RFC3339): %w", trimmed, err)
		}
		where = " WHERE ts >= ?"
		args = append(args, t.UTC().Format(storeTSLayout))
	}
	var usdCount int64
	var usdSum float64
	row := s.db.QueryRow(`SELECT COUNT(*), COALESCE(SUM(input_tokens),0), COALESCE(SUM(would_save_tokens),0),
		COALESCE(SUM(compression_tokens_before),0), COALESCE(SUM(compression_tokens_after),0),
		COALESCE(SUM(compression_tokens_before - compression_tokens_after),0),
		COALESCE(SUM(savings_usd),0), COUNT(would_save_usd), COALESCE(SUM(would_save_usd),0),
		COALESCE(SUM(cached_input_tokens),0), COALESCE(SUM(cache_creation_input_tokens),0),
		COALESCE(SUM(CASE WHEN cache_bust <> 0 THEN 1 ELSE 0 END),0),
		COALESCE(SUM(CASE WHEN compression_eligible <> 0 THEN 1 ELSE 0 END),0)
		FROM requests`+where, args...)
	if err := row.Scan(&out.Spans, &out.TokensIn, &out.WouldSaveTokens,
		&out.CompressionTokensBefore, &out.CompressionTokensAfter, &out.CompressionTokensSaved,
		&out.SavingsUSD, &usdCount, &usdSum,
		&out.CachedInputTokens, &out.CacheCreationInputTokens,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Use a full RFC3339 timestamp with timezone: 2026-01-02T15:04:05Z or 2026-01-02T15:04:05+02:00
  2. In scripts, generate it: date -u +%Y-%m-%dT%H:%M:%SZ
  3. If you only want date granularity, expand it explicitly: 2026-01-02T00:00:00Z
  4. Catch the error and show the offending value plus the expected format to the user

Example fix

# before
caveman-proxy stats --since 2026-01-02
invalid --since "2026-01-02" (want RFC3339)

# after
caveman-proxy stats --since 2026-01-02T00:00:00Z
# or generated: --since "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
Defensive patterns

Strategy: validation

Validate before calling

if since != "" {
    if _, err := time.Parse(time.RFC3339, strings.TrimSpace(since)); err != nil {
        return fmt.Errorf("--since must be RFC3339, e.g. %s", time.Now().UTC().Add(-24*time.Hour).Format(time.RFC3339))
    }
}

Type guard

func isRFC3339(s string) bool {
    s = strings.TrimSpace(s)
    if s == "" { return true } // optional flag
    _, err := time.Parse(time.RFC3339, s)
    return err == nil
}

Try / catch

sum, err := st.ObserveSummarySince(since)
if err != nil && strings.Contains(err.Error(), "invalid --since") {
    return fmt.Errorf("bad --since: pass an RFC3339 timestamp like 2026-01-02T00:00:00Z")
}

Prevention

When it happens

Trigger: Passing a date-only string ('2026-01-02'), a legacy timestamp format ('2026-01-02 15:04:05'), a missing timezone, or garbage into the --since flag consumed by ObserveSummarySince. Empty/whitespace strings are fine (no filter); anything else must be strict RFC3339.

Common situations: Users habitually typing 'YYYY-MM-DD' from other tools' date flags; passing Unix epoch seconds or ISO-with-space from scripts; a timezone-less local timestamp like '2026-01-02T15:04:05' (RFC3339 requires an offset); copy-pasting a timestamp that contains a trailing space or quote.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/aabfa526b9b0d580. Report an issue: GitHub.