{"record":{"id":"dff452989abf6f86","repo":"Budibase/budibase","slug":"expected-at-least-one-valid-date-received-a","errorCode":null,"errorMessage":"Expected at least one valid date, received '${a}' and '${b}'","messagePattern":"Expected at least one valid date, received '(.+?)' and '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/ai/agentLogs/shared.ts","lineNumber":153,"sourceCode":"    return undefined\n  }\n\n  return parsedDate\n}\n\nexport function parseDateOrThrow(value: string, label: string): string {\n  const parsedDate = parseDate(value)\n  if (!parsedDate) {\n    throw new Error(`Invalid ${label}: ${value}`)\n  }\n  return parsedDate.toISOString()\n}\n\nexport function minDate(a?: string, b?: string): string {\n  const firstDate = parseDate(a)\n  const secondDate = parseDate(b)\n  if (!firstDate && !secondDate) {\n    throw new Error(\n      `Expected at least one valid date, received '${a}' and '${b}'`\n    )\n  }\n  if (!firstDate) return secondDate!.toISOString()\n  if (!secondDate) return firstDate.toISOString()\n\n  return firstDate <= secondDate\n    ? firstDate.toISOString()\n    : secondDate.toISOString()\n}\n\nexport function maxDate(a?: string, b?: string): string {\n  const firstDate = parseDate(a)\n  const secondDate = parseDate(b)\n  if (!firstDate && !secondDate) {\n    throw new Error(\n      `Expected at least one valid date, received '${a}' and '${b}'`\n    )","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/agentLogs/shared.ts#L135-L171","documentation":"minDate returns the earliest of two optional date strings and throws when neither can be parsed, guaranteeing a non-optional ISO return value. It is called while building session rows (addSessionLog, startTime), so this surfaces when the underlying record's start timestamp is unparseable.","triggerScenarios":"addSessionLog invoked with a LiteLLM session/request record whose startTime field is missing, null, or in an unparseable format — both a and b fail parseDate simultaneously.","commonSituations":"LiteLLM records missing the expected timestamp field after a schema/version change; corrupted or hand-edited log rows; timestamps stored in an unexpected format (epoch millis instead of ISO).","solutions":["Inspect the agent-log record feeding addSessionLog and confirm its timestamp fields are valid ISO strings","Check for a LiteLLM version change that renamed or reformatted timestamp fields and update the mapping","Sanitize/normalize timestamps when writing rows so unparseable values never reach minDate","Wrap the caller in error handling that skips rows with invalid timestamps instead of failing the whole session build"],"exampleFix":"// before\nconst start = minDate(record.startTime, session.startTime)\n// after\nconst parsedStart = parseDate(record.startTime) ?? parseDate(session.startTime)\nif (!parsedStart) return // skip malformed row\nconst start = parsedStart.toISOString()","handlingStrategy":"type-guard","validationCode":"if (!isValidDateParam(record.startTime) && !isValidDateParam(session.startTime)) {\n  return // skip malformed row before calling addSessionLog\n}","typeGuard":"function hasValidTimestamp(v: unknown): v is string {\n  return typeof v === \"string\" && !Number.isNaN(Date.parse(v))\n}","tryCatchPattern":"try {\n  addSessionLog(session, record)\n} catch (err) {\n  if ((err as Error).message.includes(\"valid date\")) {\n    console.error(\"Skipping session row with invalid timestamps\", record)\n    return\n  }\n  throw err\n}","preventionTips":["Normalize timestamps to ISO at ingestion","Map LiteLLM timestamp fields after every version bump","Skip/log malformed rows instead of failing the batch","Validate records before building sessions"],"tags":["validation","dates","agent-logs","data-integrity"],"backgroundTag":"invalid-date-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}