amruthpillai/reactive-resume · error · ORPCError
BAD_REQUEST
BAD_REQUEST
Error message
Date must use YYYY-MM-DD format.
What it means
Thrown by atFromDateString() when a date string passed to an application timeline operation cannot split on '-' into three non-zero numeric components (year, month, day). The applications service requires calendar dates as strict 'YYYY-MM-DD' strings so it can pin UTC time-of-day to 12:00:00Z while preserving any existing hours/minutes. This is the first of two format guards — it catches structurally malformed input before any Date is constructed.
Source
Thrown at packages/api/src/features/applications/service.ts:22
ApplicationTimelineEntry,
Contact,
} from "@reactive-resume/schema/applications/data";
import type { ApplicationDocumentKind } from "../../dto/application";
import { ORPCError } from "@orpc/client";
import { and, arrayContains, desc, eq, inArray, sql } from "drizzle-orm";
import { db } from "@reactive-resume/db/client";
import * as schema from "@reactive-resume/db/schema";
import { generateId } from "@reactive-resume/utils/string";
import { resumeService } from "../resume/service";
import { getStorageService, uploadFile } from "../storage/service";
function timelineDate(value: Date | string): Date {
return value instanceof Date ? value : new Date(value);
}
function atFromDateString(date: string, existing?: Date | string): Date {
const [year, month, day] = date.split("-").map(Number);
if (!year || !month || !day) throw new ORPCError("BAD_REQUEST", { message: "Date must use YYYY-MM-DD format." });
const existingDate = existing ? timelineDate(existing) : undefined;
const parsed = new Date(
Date.UTC(
year,
month - 1,
day,
existingDate?.getUTCHours() ?? 12,
existingDate?.getUTCMinutes() ?? 0,
existingDate?.getUTCSeconds() ?? 0,
existingDate?.getUTCMilliseconds() ?? 0,
),
);
if (timelineDay(parsed) !== date) throw new ORPCError("BAD_REQUEST", { message: "Date must use YYYY-MM-DD format." });
return parsed;
}View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Normalize every date to 'YYYY-MM-DD' (e.g. new Date(value).toISOString().slice(0,10)) before calling create/importMany/addNote/updateTimelineEntry.
- If the source is a Date object, format it with Date.UTC(...) then toISOString().slice(0,10).
- Strip any time component or timezone offset before submission.
Example fix
// before
applicationService.create({ ..., stageEnteredAt: '01/15/2024' });
// after
const d = new Date('2024-01-15');
const stageEnteredAt = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())).toISOString().slice(0,10);
applicationService.create({ ..., stageEnteredAt }); Defensive patterns
Strategy: validation
Validate before calling
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
function toIsoDate(value: string | Date): string {
const d = value instanceof Date ? value : new Date(value);
const iso = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())).toISOString().slice(0, 10);
if (!ISO_DATE.test(iso)) throw new Error(`Not a YYYY-MM-DD date: ${String(value)}`);
return iso;
}
// toIsoDate('01/15/2024') -> '2024-01-15' Type guard
function isIsoDateString(value: unknown): value is string {
return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value);
} Prevention
- Always format dates with Date.UTC(...).toISOString().slice(0,10) before sending.
- Never pass locale-formatted or datetime strings to timeline endpoints.
- Centralize date formatting in one helper used by all callers.
When it happens
Trigger: Calling applicationService.create or importMany with stageEnteredAt like '2024/01/15', 'Jan 15 2024', '2024-1', '' or 'abc'; or addNote/updateTimelineEntry with a date field in any non-dashed, time-bearing, or incomplete shape.
Common situations: A frontend date picker emitting locale-formatted strings (en-US 'MM/DD/YYYY'), an importer mapping spreadsheet dates verbatim, or a test fixture using ISO datetime ('2024-01-15T10:00:00Z') instead of date-only.
Related errors
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/2f452c1a8c58d98c.
Report an issue: GitHub.