can1357/oh-my-pi · warning · RangeError
Invalid ISO datetime: empty string
Error message
Invalid ISO datetime: empty string
What it means
parseIsoDateTimeUtc() normalizes ISO datetime strings to UTC dates, appending T00:00:00Z for date-only values and Z when no timezone is present. An empty (or whitespace-only) string has no parseable datetime, so it throws a RangeError with a dedicated message distinct from the generic invalid-format error.
Source
Thrown at packages/mnemopi/src/util/datetime.ts:12
import { recencyHalflifeHours } from "../config";
import { LruCache } from "./lru";
const TZ_RE = /(?:Z|[+-]\d\d:?\d\d)$/;
const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
const TS_CACHE = new LruCache<string, Date>(2000);
export type QueryTime = string | Date | null | undefined;
export function parseIsoDateTimeUtc(value: string): Date {
let text = value.trim();
if (!text) throw new RangeError("Invalid ISO datetime: empty string");
if (DATE_ONLY_RE.test(text)) text += "T00:00:00Z";
else if (!TZ_RE.test(text)) text += "Z";
const date = new Date(text);
if (Number.isNaN(date.getTime())) throw new RangeError(`Invalid ISO datetime: ${value}`);
return date;
}
export function normalizeDateTimeUtc(value: Date): Date {
const time = value.getTime();
if (Number.isNaN(time)) throw new RangeError("Invalid Date");
return new Date(time);
}
export function parseQueryTime(value: QueryTime): Date {
if (value === null || value === undefined) return new Date();
return typeof value === "string" ? parseIsoDateTimeUtc(value) : normalizeDateTimeUtc(value);
}
View on GitHub (pinned to 9690622007)
Solutions
- Omit the field entirely instead of sending an empty string so parseQueryTime falls back to now.
- Coerce empty/whitespace values to undefined/null before parsing: value?.trim() ? value : undefined.
- Validate user/config input at the boundary and reject empty datetimes with a clear client-side message.
- If an empty value should mean 'beginning of time', substitute a sentinel date explicitly.
Example fix
// before
const since = parseQueryTime(searchParams.get('since')); // '' -> RangeError
// after
const raw = searchParams.get('since');
const since = parseQueryTime(raw && raw.trim() ? raw : null); // null -> now Defensive patterns
Strategy: validation
Validate before calling
function nonEmptyIsoOrNull(value) {
if (typeof value !== 'string') return null;
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null; // null -> parseQueryTime defaults to now
}
const since = parseQueryTime(nonEmptyIsoOrNull(req.query.since)); Type guard
function isNonEmptyIsoString(v) {
return typeof v === 'string' && v.trim().length > 0;
} Try / catch
try {
const t = parseIsoDateTimeUtc(raw);
} catch (err) {
if (err instanceof RangeError && err.message.includes('empty string')) {
return new Date(); // treat blank input as 'now' (parseQueryTime default)
}
throw err;
} Prevention
- Coerce empty/whitespace query params to undefined before parsing
- Reject empty datetime fields at the API boundary with a clear 400 message
- Distinguish 'absent' (omit field) from 'empty string' in client code
- Trim user input before passing to datetime parsers
When it happens
Trigger: Calling parseIsoDateTimeUtc('') or parseIsoDateTimeUtc(' ') — directly, or via parseQueryTime('') when a query/filter parameter was provided but empty.
Common situations: Empty query-string parameters (since=), empty env vars or config fields interpolated into time filters, API clients sending "" instead of omitting the field, or .trim() results from blank user input.
Related errors
- Invalid ISO datetime: ${value}
- Invalid Date
- Audio gain must be a finite non-negative number
- Invalid ISO datetime: ${value}
- Invalid CAB archive: file has an invalid DOS timestamp
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/841babf37b95e31a.
Report an issue: GitHub.