koala73/worldmonitor · error · ConvexError
digestTimezone must be a valid IANA timezone (e.g. America/N
Error message
digestTimezone must be a valid IANA timezone (e.g. America/New_York)
What it means
Thrown by setDigestSettings when args.digestTimezone is present but Intl.DateTimeFormat rejects it as a timeZone option — i.e. it is not a valid IANA timezone identifier (e.g. 'America/New_York', 'Europe/London'). The check uses the runtime's Intl database, so the validation is as authoritative as the host environment.
Source
Thrown at convex/alertRules.ts:293
variant: v.string(),
digestMode: digestModeValidator,
digestHour: v.optional(v.number()),
digestTimezone: v.optional(v.string()),
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new ConvexError("UNAUTHENTICATED");
const userId = identity.subject;
await assertProEntitlement(ctx, userId);
if (args.digestHour !== undefined && (args.digestHour < 0 || args.digestHour > 23 || !Number.isInteger(args.digestHour))) {
throw new ConvexError("digestHour must be an integer 0–23");
}
if (args.digestTimezone !== undefined) {
try {
Intl.DateTimeFormat(undefined, { timeZone: args.digestTimezone });
} catch {
throw new ConvexError("digestTimezone must be a valid IANA timezone (e.g. America/New_York)");
}
}
const existing = await ctx.db
.query("alertRules")
.withIndex("by_user_variant", (q) =>
q.eq("userId", userId).eq("variant", args.variant),
)
.unique();
const pair = resolveEffectivePair({
incomingDigestMode: args.digestMode,
existing: existing ?? undefined,
});
assertCompatibleDeliveryMode(pair);
const now = Date.now();
const patch = {View on GitHub (pinned to ffec79ac33)
Solutions
- Resolve the IANA timezone client-side via Intl.DateTimeFormat().resolvedOptions().timeZone and pass that value.
- If accepting user input, validate against a known IANA list (e.g. Intl.supportedValuesOf('timeZone')) before submitting.
- Trim whitespace before submission.
Example fix
// before
await setDigestSettings({ variant, digestMode: 'daily', digestTimezone: 'EST' });
// after
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; // e.g. 'America/New_York'
await setDigestSettings({ variant, digestMode: 'daily', digestTimezone: tz }); Defensive patterns
Strategy: validation
Validate before calling
function isValidIanaTimezone(tz) {
if (typeof tz !== 'string') return false;
try {
Intl.DateTimeFormat(undefined, { timeZone: tz });
return true;
} catch {
return false;
}
}
if (args.digestTimezone && !isValidIanaTimezone(args.digestTimezone)) {
// reject before submit
} Type guard
function isIanaTimezone(tz): tz is string {
if (typeof tz !== 'string') return false;
try { Intl.DateTimeFormat(undefined, { timeZone: tz }); return true; }
catch { return false; }
} Try / catch
try {
await setDigestSettings(args);
} catch (e) {
if (e instanceof ConvexError && /valid IANA timezone/.test(String(e.message))) {
// fall back to browser tz: args.digestTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone
} else throw e;
} Prevention
- Derive timezone from Intl.DateTimeFormat().resolvedOptions().timeZone rather than offsets.
- For user-supplied zones, validate against Intl.supportedValuesOf('timeZone').
- Trim whitespace before submission.
When it happens
Trigger: Calling setDigestSettings with digestTimezone set to an abbreviation ('EST', 'PST'), a fixed offset ('UTC-5', '+05:30'), a misspelled zone ('America/New_York '), or any non-IANA string.
Common situations: Client deriving timezone from Date.getTimezoneOffset() and converting to an offset string instead of resolving the IANA name; user typing a custom timezone; trailing whitespace; copy-paste from a non-IANA source.
Related errors
- digestHour must be an integer 0–23
- quietHoursStart must be an integer 0–23
- quietHoursEnd must be an integer 0–23
- quietHoursTimezone must be a valid IANA timezone (e.g. Ameri
- INCOMPATIBLE_DELIVERY
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/0ab8213294691c0a.
Report an issue: GitHub.