koala73/worldmonitor · error · ConvexError
quietHoursEnd must be an integer 0–23
Error message
quietHoursEnd must be an integer 0–23
What it means
Thrown by validateQuietHoursArgs when args.quietHoursEnd is present but outside [0,23] or is not an integer. quietHoursEnd is the hour-of-day (0–23) at which the do-not-disturb window ends. Same validation shape as quietHoursStart.
Source
Thrown at convex/alertRules.ts:428
const QUIET_HOURS_ARGS = {
variant: v.string(),
quietHoursEnabled: v.boolean(),
quietHoursStart: v.optional(v.number()),
quietHoursEnd: v.optional(v.number()),
quietHoursTimezone: v.optional(v.string()),
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);View on GitHub (pinned to ffec79ac33)
Solutions
- Coerce quietHoursEnd to an integer in [0,23] before submitting.
- Ensure the picker uses 24-hour 0-based output and wraps 24→0.
- Ensure quietHoursEnd differs from quietHoursStart when the window is enabled (separate check at line 461/571).
Example fix
// before
await setQuietHours({ variant, quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 24 });
// after
await setQuietHours({ variant, quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 0 }); // midnight wrap Defensive patterns
Strategy: validation
Validate before calling
function isValidHour(h) {
return typeof h === 'number' && Number.isInteger(h) && h >= 0 && h <= 23;
}
if (args.quietHoursEnd !== undefined && !isValidHour(args.quietHoursEnd)) {
// surface error before submit
} Type guard
function isQuietHour(n): n is number {
return typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 23;
} Try / catch
try {
await setQuietHours(args);
} catch (e) {
if (e instanceof ConvexError && /quietHoursEnd must be an integer/.test(String(e.message))) {
// coerce end and retry
} else throw e;
} Prevention
- Use a 0-based 24-hour integer picker that wraps 24→0.
- Coerce with Math.floor and clamp to [0,23].
- Verify end differs from start when quiet hours are enabled.
When it happens
Trigger: Calling setQuietHours or setQuietHoursForUser with quietHoursEnd = 24, -1, 6.25, or any non-integer.
Common situations: UI picker emitting 1-based or 12-hour values; passing a Date.getHours()+1 that overflows to 24 at 23:00; float inputs.
Related errors
- quietHoursStart must be an integer 0–23
- digestHour must be an integer 0–23
- digestTimezone must be a valid IANA timezone (e.g. America/N
- quietHoursTimezone must be a valid IANA timezone (e.g. Ameri
- quietHoursStart and quietHoursEnd must differ (same value =
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/3b60bebcee8e2cd6.
Report an issue: GitHub.