can1357/oh-my-pi · error · TypeError

queryTime must be null, an ISO date string, or a valid Date

Error message

queryTime must be null, an ISO date string, or a valid Date

What it means

parseQueryTime in recall.ts throws a TypeError when the `queryTime` option is neither null/undefined, a Date, nor a string parseable as ISO-8601 with UTC/offset semantics. Plain date strings get midnight-UTC appended; strings lacking Z/offset get Z appended; anything still unparseable is rejected rather than silently defaulting to now.

Source

Thrown at packages/mnemopi/src/core/beam/recall.ts:412

	return Math.exp(-ageHours / Math.max(halfLifeHours, 0.001));
}

export function parseQueryTime(value: RecallOptionsInternal["queryTime"]): Date {
	if (value == null) return new Date();
	if (value instanceof Date) {
		if (!Number.isFinite(value.getTime())) throw new RangeError("Invalid query time");
		return value;
	}
	if (typeof value === "string") {
		const normalized = /^\d{4}-\d{2}-\d{2}$/.test(value)
			? `${value}T00:00:00.000Z`
			: /(?:Z|[+-]\d{2}:?\d{2})$/.test(value)
				? value
				: `${value}Z`;
		const parsed = new Date(normalized);
		if (Number.isFinite(parsed.getTime())) return parsed;
	}
	throw new TypeError("queryTime must be null, an ISO date string, or a valid Date");
}

export function temporalBoost(timestamp: unknown, queryTime: Date, halfLifeHours: number): number {
	const raw = asString(timestamp);
	if (raw.length === 0) return 0;
	const parsed = Date.parse(raw);
	if (!Number.isFinite(parsed)) return 0;
	const distanceHours = Math.max(0, queryTime.getTime() - parsed) / 3_600_000;
	return Math.exp(-distanceHours / Math.max(halfLifeHours, 0.001));
}

function inferTemporalOptions(query: string, options: RecallOptionsInternal): RecallOptionsInternal {
	const copy: RecallOptionsInternal = { ...options };
	const info = extractTemporal(query, options.queryTime ?? undefined);
	if (info.event_date !== null) {
		copy.queryTime ??= info.event_date;
		copy.temporalWeight ??= 0.35;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap numbers in `new Date(epochMs)` and verify it is valid before passing.
  2. Convert display-formatted dates to ISO-8601 (`2024-03-05` or `2024-03-05T00:00:00Z`).
  3. Omit queryTime (or pass null) if you intend 'now' — do not pass an empty string.

Example fix

// before
recall(query, { queryTime: Date.now() }); // number not accepted
// after
recall(query, { queryTime: new Date(Date.now()) });
Defensive patterns

Strategy: type-guard

Validate before calling

function asQueryTime(v: unknown): string | Date | null {
  if (v == null) return null;
  if (v instanceof Date) return v;
  if (typeof v === "number") return new Date(v); // convert epoch ms
  if (typeof v === "string" && /^\d{4}-\d{2}-\d{2}/.test(v)) return v;
  throw new TypeError(`Unsupported queryTime: ${String(v)}`);
}

Type guard

function isQueryTime(v: unknown): v is string | Date | null {
  return v == null || v instanceof Date || typeof v === "string";
}

Try / catch

try {
  return recall(query, { queryTime });
} catch (e) {
  if (e instanceof TypeError && e.message.startsWith("queryTime must be")) {
    return recall(query, { queryTime: null }); // 'now'
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing queryTime as a number (epoch ms), a non-ISO string like `"March 5, 2024"`, or an ISO string with an unparseable component, to recall options consumed by decay/scoreCandidate/eventBoost.

Common situations: Passing epoch milliseconds (number) where a string/Date is expected; locale-formatted dates from UI date pickers; undefined-vs-null confusion is fine (treated as now), but wrong types are not.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8fe73fd25a30d3b3. Report an issue: GitHub.