expo/expo · error · CommandError
INVALID_OPTIONS
INVALID_OPTIONS
Error message
Cannot publish with sdkVersion UNVERSIONED.
What it means
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.
Source
Thrown at packages/@expo/cli/src/export/getPublicExpoManifest.ts:27
/** Get the public Expo manifest from the local project config. */
export async function getPublicExpoManifestAsync(
projectRoot: string,
{ skipValidation }: { skipValidation?: boolean } = {}
): Promise<ExpoConfig & { locales: LocaleMap; sdkVersion: string }> {
// Read the config in public mode which strips the `hooks`.
const { exp } = getConfig(projectRoot, {
isPublicConfig: true,
// This shouldn't be needed since the CLI is vendored in `expo`.
skipSDKVersionRequirement: true,
});
// Only allow projects to be published with UNVERSIONED if a correct token is set in env
if (
!skipValidation &&
exp.sdkVersion === 'UNVERSIONED' &&
!env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN
) {
throw new CommandError('INVALID_OPTIONS', 'Cannot publish with sdkVersion UNVERSIONED.');
}
return {
...exp,
locales: await getResolvedLocalesAsync(projectRoot, exp),
sdkVersion: exp.sdkVersion!,
};
}
View on GitHub (pinned to b09195aac2)
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`).
Example fix
// before — using a source build of expo, sdkVersion resolves to UNVERSIONED expo publish // after — install a published version npm install --save-dev expo@latest npx expo publish
Defensive patterns
Strategy: validation
Validate before calling
import { getConfig } from '@expo/config';
function assertVersionedSdk(projectRoot: string): void {
const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });
if (exp.sdkVersion === 'UNVERSIONED' && !process.env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN) {
throw new Error('Refusing to publish UNVERSIONED sdkVersion; install a published expo package');
}
} Type guard
function isVersionedSdk(v: unknown): boolean {
return typeof v === 'string' && v !== 'UNVERSIONED' && /^\d+\.\d+\.\d+/.test(v);
} Try / catch
try {
await getPublicExpoManifestAsync(projectRoot, { skipValidation: false });
} catch (e) {
if (e instanceof CommandError && e.code === 'INVALID_OPTIONS' && /UNVERSIONED/.test(e.message)) {
// switch to a published CLI, or use expo export (skipValidation) instead of publish
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid option: --type ${options.type}. Valid options are: p
- Invalid files: ${diff.join(', ')}. Allowed: ${TEMPLATES.map(
- Expected boolean, got ${typeof val}
- Missing required argument: --platform
- Missing required argument: --bundle-output
AI-assisted analysis of expo/expo@b09195aac2 (2026-08-12).
Data as JSON: /api/errors/eb3dcc5f475e7071.
Report an issue: GitHub.