{"record":{"id":"905be99570647818","repo":"stablyai/orca","slug":"hourly-build-timestamp-is-invalid","errorCode":null,"errorMessage":"Hourly build timestamp is invalid.","messagePattern":"Hourly build timestamp is invalid\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/hourly-build-version.mjs","lineNumber":18,"sourceCode":"import { execFileSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { formatReleaseTitleTimestamp } from './release-title-timestamp.mjs'\nimport {\n  readPublishedVersionsFromEnv,\n  resolveDevChannelBaseVersion\n} from './dev-channel-base-version.mjs'\n\n/** `1.4.160-hourly.202607281400` — UTC to the minute, so tags sort chronologically\n *  by semver and every build is uniquely versioned. */\nexport function createHourlyBuildVersion(baseVersion, date) {\n  const match = /^(\\d+\\.\\d+\\.\\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(baseVersion)\n  if (!match) {\n    throw new Error(`Package version is not valid semver: ${baseVersion}`)\n  }\n  if (!(date instanceof Date) || Number.isNaN(date.getTime())) {\n    throw new Error('Hourly build timestamp is invalid.')\n  }\n  const pad = (value, width = 2) => String(value).padStart(width, '0')\n  const stamp = [\n    pad(date.getUTCFullYear(), 4),\n    pad(date.getUTCMonth() + 1),\n    pad(date.getUTCDate()),\n    pad(date.getUTCHours()),\n    pad(date.getUTCMinutes())\n  ].join('')\n  // Why: drop any -rc.N tail. Keeping it makes every hourly semver-NEWER than the\n  // RC it was cut from (1.4.160-rc.3-hourly.X > 1.4.160-rc.3), which would let an\n  // ordinary RC-channel check offer untested hourly builds to RC users. Stripping\n  // to the base parks hourlies below both rc.N and stable ('hourly' < 'rc'\n  // alphabetically), reachable only by an explicit pinned jump.\n  return `${match[1]}-hourly.${stamp}`\n}\n\n/**","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/hourly-build-version.mjs#L1-L36","documentation":"Thrown by createHourlyBuildVersion() when the date argument is not a Date instance or is an Invalid Date (getTime() returns NaN). The timestamp is formatted to UTC minute precision for the hourly version suffix, so a usable Date is required.","triggerScenarios":"Passing a string/number/null instead of a Date, passing `new Date('invalid')`, or passing a Date constructed from a bad timestamp.","commonSituations":"Caller passing a raw ISO string instead of `new Date(isoString)`, a Date constructed from undefined/NaN, or a timezone-parsing edge case producing Invalid Date.","solutions":["Pass a valid Date object: `new Date()` or `new Date(validTimestamp)`.","If converting from a string, construct the Date first and check `Number.isNaN(date.getTime())`.","Default the parameter (`now = new Date()`) so callers can omit it."],"exampleFix":"// before\ncreateHourlyBuildVersion('1.4.0', '2026-07-28T14:00:00Z')  // string, throws\n// after\ncreateHourlyBuildVersion('1.4.0', new Date('2026-07-28T14:00:00Z'))","handlingStrategy":"type-guard","validationCode":"function assertValidDate(d: unknown): asserts d is Date {\n  if (!(d instanceof Date) || Number.isNaN(d.getTime())) {\n    throw new Error('Hourly build timestamp is invalid.')\n  }\n}\nassertValidDate(date)","typeGuard":"const isValidDate = (d: unknown): d is Date =>\n  d instanceof Date && !Number.isNaN(d.getTime())","tryCatchPattern":null,"preventionTips":["Pass `new Date()` or a Date constructed from a validated timestamp — never a raw string.","When accepting date input at an API boundary, validate with isValidDate before forwarding."],"tags":["release","date","versioning","validation","type-guard"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}