{"record":{"id":"807a7695f2f39e6c","repo":"stablyai/orca","slug":"adhoc-build-timestamp-is-invalid","errorCode":null,"errorMessage":"Adhoc build timestamp is invalid.","messagePattern":"Adhoc build timestamp is invalid\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/scripts/adhoc-build-version.mjs","lineNumber":29,"sourceCode":"export const ADHOC_LABEL_MAX_LENGTH = 32\n\n/**\n * `1.4.160-adhoc.20260728140533` — UTC to the second, so tags sort\n * chronologically by semver and every build is uniquely versioned.\n *\n * Why seconds when hourly uses minutes: hourly runs under a concurrency group and\n * cannot overlap itself. Adhoc builds are dispatched on demand, so two people\n * cutting from different branches in the same minute is ordinary — and a\n * minute-resolution tag would collide and fail the second build after its whole\n * pack-and-notarize run.\n */\nexport function createAdhocBuildVersion(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('Adhoc 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    pad(date.getUTCSeconds())\n  ].join('')\n  // Why: drop any -rc.N tail, same as hourly. Keeping it would make every adhoc\n  // build semver-NEWER than the RC it was cut from, letting an ordinary\n  // RC-channel check offer an unreviewed branch build to RC users. Stripping to\n  // the base parks adhoc below rc.N, hourly, and stable ('adhoc' sorts first\n  // alphabetically), reachable only by an explicit pinned jump.\n  return `${match[1]}-adhoc.${stamp}`\n}\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/adhoc-build-version.mjs#L11-L47","documentation":"Thrown by createAdhocBuildVersion when the date argument is not a Date instance or is an Invalid Date (getTime() returns NaN). The adhoc version stamps UTC time down to the second, so a real timestamp is mandatory; this guard rejects undefined/null/numbers/strings and Date objects constructed from unparseable input.","triggerScenarios":"getAdhocBuildIdentity was called with a custom now that returned undefined; a test passed a string or epoch number instead of new Date(); the workflow injected a malformed timestamp through an env var that was wrapped in new Date(badString).","commonSituations":"Refactor changed the default parameter from new Date() to something else; ORCA timestamp env var formatted as 'YYYY-MM-DD HH:mm' (unparseable by Date constructor in some locales); mock clock returned null in a test.","solutions":["Pass a real Date: createAdhocBuildVersion(base, new Date()) or new Date(Date.now()).","When constructing from an env-provided timestamp, parse it explicitly (e.g. new Date(ms) for an epoch) and validate before calling.","Restore the default parameter 'now = new Date()' in getAdhocBuildIdentity if a refactor removed it.","In tests, inject a frozen Date via sinon.useFakeTimers or a wrapper rather than passing a string."],"exampleFix":"// before\n// const id = getAdhocBuildIdentity('2026-07-28 14:05:33', label, published)\n// -> 'Adhoc build timestamp is invalid.'\n\n// after\n// const id = getAdhocBuildIdentity(new Date('2026-07-28T14:05:33Z'), label, published)","handlingStrategy":"type-guard","validationCode":"function assertValidTimestamp(date) {\n  if (!(date instanceof Date) || Number.isNaN(date.getTime())) {\n    throw new Error('Adhoc build timestamp must be a valid Date instance.')\n  }\n}\n// assertValidTimestamp(date) before createAdhocBuildVersion","typeGuard":"function isValidTimestamp(value) {\n  return value instanceof Date && !Number.isNaN(value.getTime())\n}","tryCatchPattern":null,"preventionTips":["Always pass new Date() (or new Date(Date.now())) — never a string or epoch number — to createAdhocBuildVersion.","When parsing a timestamp from an env var, construct the Date and validate it before forwarding.","Keep the 'now = new Date()' default parameter in getAdhocBuildIdentity intact; do not refactor it away."],"tags":["release","adhoc-build","date","validation","versioning"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}