twentyhq/twenty · critical · Error
defineApplication in ${configPath} must declare a universalI
Error message
defineApplication in ${configPath} must declare a universalIdentifier before generating entities. What it means
Thrown by the Twenty SDK CLI when it found an application config file at a recognized path but the extracted config object has no universalIdentifier property. The universalIdentifier is the root seed for all deterministically generated identifiers, so its absence makes code generation impossible. This is a stricter check than error 82 — the file exists but is incomplete.
Source
Thrown at packages/twenty-sdk/src/cli/utilities/application/get-application-universal-identifier-or-throw.ts:32
export const getApplicationUniversalIdentifierOrThrow = async (
appPath: string,
): Promise<string> => {
const configPath = await findAppConfigPath(appPath);
if (configPath === null) {
throw new Error(
`Could not find the application config file (expected one of: ${APP_CONFIG_CANDIDATE_PATHS.join(', ')}). Create it with defineApplication({ universalIdentifier: ... }) before generating entities.`,
);
}
const { config } =
await extractManifestFromFile<ApplicationConfigWithUniversalIdentifier>({
appPath,
filePath: configPath,
});
if (!config?.universalIdentifier) {
throw new Error(
`defineApplication in ${configPath} must declare a universalIdentifier before generating entities.`,
);
}
return config.universalIdentifier;
};
View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Add universalIdentifier to your defineApplication call: defineApplication({ universalIdentifier: 'com.yourcompany.yourapp' }).
- Use a reverse-DNS format for the identifier to ensure global uniqueness (e.g. com.acme.sales-tools).
- Ensure the value is a non-empty string — empty strings are falsy and will fail the !config?.universalIdentifier check.
- Verify the defineApplication call is exported as default from the config file so the manifest extractor can read it.
Example fix
// before — src/application.config.ts
import { defineApplication } from 'twenty-sdk';
export default defineApplication({ name: 'MyApp' });
// after
import { defineApplication } from 'twenty-sdk';
export default defineApplication({
universalIdentifier: 'com.acme.sales-tools',
name: 'MyApp',
}); Defensive patterns
Strategy: validation
Validate before calling
import { extractManifestFromFile } from '@/cli/utilities/build/manifest/manifest-extract-config-from-file';
const config = (await extractManifestFromFile({
appPath,
filePath: configPath,
})).config;
if (!config?.universalIdentifier) {
throw new Error('Add universalIdentifier to your defineApplication call.');
} Type guard
const hasUniversalIdentifier = (
config: unknown,
): config is { universalIdentifier: string } => {
return (
typeof config === 'object' &&
config !== null &&
'universalIdentifier' in config &&
typeof (config as { universalIdentifier: unknown }).universalIdentifier === 'string' &&
(config as { universalIdentifier: string }).universalIdentifier.length > 0
);
}; Try / catch
try {
const id = await getApplicationUniversalIdentifierOrThrow(appPath);
} catch (error) {
if (error instanceof Error && error.message.includes('must declare a universalIdentifier')) {
console.error('Add universalIdentifier to src/application.config.ts');
process.exit(1);
}
throw error;
} Prevention
- Use a reverse-DNS format for universalIdentifier to guarantee uniqueness.
- Verify the config file exports defineApplication result as default.
- Run a quick check after scaffolding: ensure universalIdentifier is a non-empty string.
When it happens
Trigger: The config file is found but defineApplication was called without a universalIdentifier argument, or the config object extracted from the manifest is empty/undefined. This can also happen if extractManifestFromFile cannot statically analyze the defineApplication call's arguments.
Common situations: Developer created the config file with defineApplication({ name: 'MyApp' }) but forgot universalIdentifier. Or they used defineApplication() with no arguments. The file was created from an outdated template that didn't include the field.
Related errors
- Could not find the application config file (expected one of:
- JSONC parse errors:\n${errorMessages.join('\n')}
- Directory ${appDirectory} already exists and is not empty
- upsertRowLevelPermissionPredicates returned fewer than 2 pre
- upsertRowLevelPermissionPredicates returned no predicate for
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/81e954d514d86fbf.
Report an issue: GitHub.