{"record":{"id":"57fd06397e95f2fa","repo":"immich-app/immich","slug":"cannot-derive-a-mobile-build-number-from-version-format","errorCode":null,"errorMessage":"Cannot derive a mobile build number from ${version.format()}","messagePattern":"Cannot derive a mobile build number from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/scripts/src/commands/release.ts","lineNumber":203,"sourceCode":"  return newVersionRaw;\n};\n\nconst RADIX = 100;\nconst STABLE = RADIX - 1;\n\nexport const getMobileBuild = (version: SemVer) => {\n  const { major, minor, patch, prerelease } = version;\n  const candidate = prerelease[1];\n  const digit = typeof candidate === 'number' ? candidate : STABLE;\n\n  const valid =\n    major < RADIX &&\n    minor < RADIX &&\n    patch < RADIX &&\n    (prerelease.length === 0 || digit < STABLE);\n\n  if (!valid) {\n    throw new Error(\n      `Cannot derive a mobile build number from ${version.format()}`,\n    );\n  }\n\n  return (\n    major * Math.pow(RADIX, 3) +\n    minor * Math.pow(RADIX, 2) +\n    patch * RADIX +\n    digit\n  );\n};\n\nconst pump = (path: string, pattern: RegExp, replacement: string) => {\n  const file = new TextFile(path);\n  const update = file.read().replace(pattern, replacement);\n  file.write(update);\n};\n","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/packages/scripts/src/commands/release.ts#L185-L221","documentation":"getMobileBuild derives a monotonic integer mobile build number from a semver as major*100^3 + minor*100^2 + patch*100 + digit. Each component must be < 100 and a prerelease digit must be < 99, otherwise the packed integer would be ambiguous; when those bounds fail, this Error is thrown with the formatted version.","triggerScenarios":"Releasing an app version where major, minor, or patch >= 100, or a prerelease whose second element (the numeric identifier, e.g. the .5 in 1.2.3-beta.5) is >= 99.","commonSituations":"Very mature packages reaching x.100.0 or y >= 100 patch, or prerelease tags with large numeric identifiers; usually in long-lived mobile release automation for Immich's iOS/Android builds.","solutions":["Keep released version components below 100 (bump major earlier, e.g. 0.99.x => 1.0.0).","Use small numeric prerelease identifiers (beta.1, beta.2, ...) instead of large ones.","If a component must exceed 99, change the packing scheme/RADIX in getMobileBuild and re-derive build numbers consistently for both stores.","Verify the version passed to getMobileBuild is the intended one; a malformed version can overflow a component."],"exampleFix":"// before\n\"version\": \"1.101.0\"  // patch/minor >= 100 => throws\n// after\n\"version\": \"2.0.0\"    // components < 100, build = 2*1000000","handlingStrategy":"validation","validationCode":"const isMobileSafe = (v: SemVer) =>\n  v.major < 100 && v.minor < 100 && v.patch < 100 &&\n  (v.prerelease.length === 0 || (v.prerelease[1] as number ?? 99) < 99);\nif (!isMobileSafe(version)) failFast(`cannot pack ${v.format()} into a build number`);","typeGuard":null,"tryCatchPattern":"try {\n  const build = getMobileBuild(version);\n} catch (e) {\n  if (String(e).startsWith('Cannot derive a mobile build number')) {\n    console.error('bump major (or shrink prerelease id) before releasing mobile');\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Keep major/minor/patch below 100 in mobile release planning","Use small numeric prerelease identifiers (beta.1..98)","Validate the packed build number in CI before store submission"],"tags":["semver","mobile","release-tooling","value-out-of-range"],"backgroundTag":"value-out-of-range","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}