{"record":{"id":"eb3dcc5f475e7071","repo":"expo/expo","slug":"invalid-options","errorCode":"INVALID_OPTIONS","errorMessage":"Cannot publish with sdkVersion UNVERSIONED.","messagePattern":"Cannot publish with sdkVersion UNVERSIONED\\.","errorType":"validation","errorClass":"CommandError","httpStatus":null,"severity":"error","filePath":"packages/@expo/cli/src/export/getPublicExpoManifest.ts","lineNumber":27,"sourceCode":"/** Get the public Expo manifest from the local project config. */\nexport async function getPublicExpoManifestAsync(\n  projectRoot: string,\n  { skipValidation }: { skipValidation?: boolean } = {}\n): Promise<ExpoConfig & { locales: LocaleMap; sdkVersion: string }> {\n  // Read the config in public mode which strips the `hooks`.\n  const { exp } = getConfig(projectRoot, {\n    isPublicConfig: true,\n    // This shouldn't be needed since the CLI is vendored in `expo`.\n    skipSDKVersionRequirement: true,\n  });\n\n  // Only allow projects to be published with UNVERSIONED if a correct token is set in env\n  if (\n    !skipValidation &&\n    exp.sdkVersion === 'UNVERSIONED' &&\n    !env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN\n  ) {\n    throw new CommandError('INVALID_OPTIONS', 'Cannot publish with sdkVersion UNVERSIONED.');\n  }\n\n  return {\n    ...exp,\n    locales: await getResolvedLocalesAsync(projectRoot, exp),\n    sdkVersion: exp.sdkVersion!,\n  };\n}\n","sourceCodeStart":9,"sourceCodeEnd":36,"githubUrl":"https://github.com/expo/expo/blob/b09195aac27c9e4a63daaf12dcb6f00b6b2e08ee/packages/@expo/cli/src/export/getPublicExpoManifest.ts#L9-L36","documentation":"Thrown by `getPublicExpoManifestAsync` (the manifest publisher) with code `INVALID_OPTIONS` when the resolved `sdkVersion` is the literal string `UNVERSIONED` and no `EXPO_SKIP_MANIFEST_VALIDATION_TOKEN` env var is set. `UNVERSIONED` is reserved for the Expo team's internal unreleased builds; publishing such a manifest would break OTA updates for real clients. This guard blocks accidental publishes from a dev/unversioned CLI.","triggerScenarios":"Running `expo publish` (or any manifest publish) with a CLI built from source against an unreleased SDK, which reports `sdkVersion: 'UNVERSIONED'`, and no validation token in the environment. `skipValidation` (set during normal export) bypasses it.","commonSituations":"Using a monorepo that symlinks a source build of `@expo/cli`. Running a local fork of Expo. CI that inadvertently resolves to an unversioned CLI. Users who cloned expo/expo and ran their app against it.","solutions":["Install and use a published version of `@expo/cli`/`expo` matching a released SDK (`npm i -g expo@latest` or the project pin).","If you are Expo staff and legitimately need to publish an unversioned manifest, set `EXPO_SKIP_MANIFEST_VALIDATION_TOKEN` to the correct token.","Confirm `app.json` does not hardcode `sdkVersion: 'UNVERSIONED'` and that `expo` in `package.json` resolves to a versioned package.","Use `expo export` instead of `expo publish` if you only need static output (export passes `skipValidation`)."],"exampleFix":"// before — using a source build of expo, sdkVersion resolves to UNVERSIONED\nexpo publish\n\n// after — install a published version\nnpm install --save-dev expo@latest\nnpx expo publish","handlingStrategy":"validation","validationCode":"import { getConfig } from '@expo/config';\nfunction assertVersionedSdk(projectRoot: string): void {\n  const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });\n  if (exp.sdkVersion === 'UNVERSIONED' && !process.env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN) {\n    throw new Error('Refusing to publish UNVERSIONED sdkVersion; install a published expo package');\n  }\n}","typeGuard":"function isVersionedSdk(v: unknown): boolean {\n  return typeof v === 'string' && v !== 'UNVERSIONED' && /^\\d+\\.\\d+\\.\\d+/.test(v);\n}","tryCatchPattern":"try {\n  await getPublicExpoManifestAsync(projectRoot, { skipValidation: false });\n} catch (e) {\n  if (e instanceof CommandError && e.code === 'INVALID_OPTIONS' && /UNVERSIONED/.test(e.message)) {\n    // switch to a published CLI, or use expo export (skipValidation) instead of publish\n  }\n  throw e;\n}","preventionTips":["Use a published `expo` package matching a real SDK version; avoid source/fork builds for publishing.","Reserve `EXPO_SKIP_MANIFEST_VALIDATION_TOKEN` for Expo-internal unversioned publishes.","Prefer `expo export` over `expo publish` when you only need static output."],"tags":["publish","manifest","sdk-version","validation","command-error","ota"],"backgroundTag":null,"analyzedSha":"b09195aac27c9e4a63daaf12dcb6f00b6b2e08ee","analyzedAt":"2026-08-12T15:59:58.304Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}