koala73/worldmonitor · error · ConvexError
digestHour must be an integer 0–23
Error message
digestHour must be an integer 0–23
What it means
Thrown by setDigestSettings when args.digestHour is present but outside [0,23], or is not an integer. digestHour represents the hour-of-day at which a digest is delivered and must be a whole number 0–23. The validation runs after the auth/entitlement gate and before any DB read.
Source
Thrown at convex/alertRules.ts:287
}
},
});
export const setDigestSettings = mutation({
args: {
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,View on GitHub (pinned to ffec79ac33)
Solutions
- Coerce and range-check digestHour to an integer in [0,23] before submitting (use Math.floor and clamp/mod).
- Ensure the UI time picker emits 0-based hours (00:00–23:59).
- Omit digestHour entirely if you do not need to change it — the field is optional.
Example fix
// before
await setDigestSettings({ variant, digestMode: 'daily', digestHour: 24 });
// after
const hour = Math.max(0, Math.min(23, Math.floor(pickerHour)) % 24);
await setDigestSettings({ variant, digestMode: 'daily', digestHour: hour }); Defensive patterns
Strategy: validation
Validate before calling
function isValidDigestHour(h) {
return typeof h === 'number' && Number.isInteger(h) && h >= 0 && h <= 23;
}
if (args.digestHour !== undefined && !isValidDigestHour(args.digestHour)) {
// surface error, do not call mutation
} Type guard
function isDigestHour(n): n is number {
return typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 23;
} Try / catch
try {
await setDigestSettings(args);
} catch (e) {
if (e instanceof ConvexError && /digestHour must be an integer/.test(String(e.message))) {
// coerce: args.digestHour = Math.floor(args.digestHour) % 24 and retry once
} else throw e;
} Prevention
- Use a 0-based 24-hour integer picker for digest scheduling.
- Coerce with Math.floor and clamp to [0,23] before submit.
- Omit digestHour when not changing it.
When it happens
Trigger: Calling setDigestSettings with digestHour = 24, -1, 23.5, NaN, or any non-integer numeric value.
Common situations: Client passing a 1-based hour (1–24) instead of 0-based; a time picker returning minutes or fractional hours; a timezone offset calculation producing an out-of-range value; JSON deserialization of a float where an int was expected.
Related errors
- digestTimezone must be a valid IANA timezone (e.g. America/N
- quietHoursStart must be an integer 0–23
- quietHoursEnd must be an integer 0–23
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/af4698d293a2e115.
Report an issue: GitHub.