koala73/worldmonitor · error · ConvexError
quietHoursTimezone must be a valid IANA timezone (e.g…
Error message
quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)
What it means
The third check in validateQuietHoursArgs (convex/alertRules.ts:430): when quietHoursTimezone is supplied, the server constructs Intl.DateTimeFormat(undefined, { timeZone }) and any RangeError is rethrown as ConvexError 'quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)'. Only IANA zone identifiers are accepted — abbreviations and fixed offsets are not.
Solutions
- Send IANA identifiers ('America/Los_Angeles', 'Asia/Tokyo', 'UTC') or omit the field.
- Default from the browser: Intl.DateTimeFormat().resolvedOptions().timeZone.
- Validate with the same Intl.DateTimeFormat call client-side before submitting.
- Map Windows timezone names to IANA when importing from Microsoft-ecosystem data.
Example fix
// before
await mutateAPI.alertRules.setQuietHours({ variant: 'default', quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 7, quietHoursTimezone: 'PST' });
// ConvexError: quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)
// after
await mutateAPI.alertRules.setQuietHours({ variant: 'default', quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 7, quietHoursTimezone: 'America/Los_Angeles' }); Defensive patterns
Strategy: validation
Validate before calling
function isValidTimezone(tz: string): boolean {
try {
Intl.DateTimeFormat(undefined, { timeZone: tz });
return true;
} catch {
return false;
}
}
if (quietHoursTimezone !== undefined && !isValidTimezone(quietHoursTimezone)) {
setFieldError('quietHoursTimezone', 'Use an IANA zone such as America/New_York');
return;
}
await mutateAPI.alertRules.setQuietHours({ variant, quietHoursEnabled, quietHoursStart, quietHoursEnd, quietHoursTimezone }); Type guard
const isIanaTimezone = (tz: string): boolean => {
try {
Intl.DateTimeFormat(undefined, { timeZone: tz });
return true;
} catch {
return false;
}
}; Try / catch
try {
await mutateAPI.alertRules.setQuietHours(args);
} catch (err) {
if (err instanceof ConvexError && typeof err.data === 'string' && err.data.includes('quietHoursTimezone')) {
setFieldError('quietHoursTimezone', 'Use an IANA zone such as America/New_York');
return;
}
throw err;
} Prevention
- Reuse the user's digest timezone (already IANA-validated) as the quiet-hours default.
- Never accept timezone abbreviations or numeric offsets from users or imports.
- Convert Windows zone names to IANA at the import boundary.
When it happens
Trigger: setQuietHours (or setQuietHoursForUser) with quietHoursTimezone 'EST', 'CET', 'UTC+5:30', 'GMT-8', a typo'd or wrongly-cased zone name, or a Windows tz name like 'Pacific Standard Time'.
Common situations: Reusing a digest timezone string from another system that stores abbreviations; free-text timezone inputs; syncing from calendar apps that expose Windows zone names; storing offsets like '+02:00' from ISO timestamps.
Related errors
- digestTimezone must be a valid IANA timezone (e.g…
- quietHoursEnd must be an integer 0–23
- quietHoursStart and quietHoursEnd must differ (same value =…
- quietHoursStart must be an integer 0–23
- COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/e857f6094ba2d669.
Report an issue: GitHub.
Appendix: source
Thrown at convex/alertRules.ts:434
quietHoursOverride: v.optional(quietHoursOverrideValidator),
} as const;
function validateQuietHoursArgs(args: {
quietHoursStart?: number;
quietHoursEnd?: number;
quietHoursTimezone?: string;
}) {
if (args.quietHoursStart !== undefined && (args.quietHoursStart < 0 || args.quietHoursStart > 23 || !Number.isInteger(args.quietHoursStart))) {
throw new ConvexError("quietHoursStart must be an integer 0–23");
}
if (args.quietHoursEnd !== undefined && (args.quietHoursEnd < 0 || args.quietHoursEnd > 23 || !Number.isInteger(args.quietHoursEnd))) {
throw new ConvexError("quietHoursEnd must be an integer 0–23");
}
if (args.quietHoursTimezone !== undefined) {
try {
Intl.DateTimeFormat(undefined, { timeZone: args.quietHoursTimezone });
} catch {
throw new ConvexError("quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)");
}
}
}
export const setQuietHours = mutation({
args: QUIET_HOURS_ARGS,
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new ConvexError("UNAUTHENTICATED");
const userId = identity.subject;
await assertProEntitlement(ctx, userId);
validateQuietHoursArgs(args);
const existing = await ctx.db
.query("alertRules")
.withIndex("by_user_variant", (q) =>
q.eq("userId", userId).eq("variant", args.variant),
)View on GitHub (pinned to eeab0a219f)