twentyhq/twenty · critical · Error
Cannot migrate: rolePermissionFlag rows reference unknown fl
Error message
Cannot migrate: rolePermissionFlag rows reference unknown flag value(s): ${unknownFlags} What it means
Thrown by a SLOW instance-level 2.6 migration that backfills permissionFlag rows and links rolePermissionFlag to them. Before writing, it queries core.rolePermissionFlag for any 'flag' value not in the current PermissionFlagType enum (PERMISSION_FLAG_TYPES). If any unknown flag exists, it refuses to migrate and lists them. This is a deliberate fail-fast data-integrity guard: it prevents silently dropping permission grants whose flag the running code no longer recognizes.
Source
Thrown at packages/twenty-server/src/database/commands/upgrade-version-command/2-6/2-6-instance-command-slow-1778235340023-backfill-role-permission-flag-permission-flag-id.ts:23
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
import { STANDARD_PERMISSION_FLAG_DEFINITIONS } from 'src/engine/metadata-modules/permission-flag/constants/standard-permission-flag-definitions.constant';
const PERMISSION_FLAG_TYPES = Object.values(PermissionFlagType) as string[];
@RegisteredInstanceCommand('2.6.0', 1778235340023, { type: 'slow' })
export class BackfillRolePermissionFlagPermissionFlagIdSlowInstanceCommand implements SlowInstanceCommand {
async runDataMigration(dataSource: DataSource): Promise<void> {
const unknownFlagRows: { flag: string }[] = await dataSource.query(
`SELECT DISTINCT "flag" FROM "core"."rolePermissionFlag"
WHERE "flag" <> ALL($1::varchar[])`,
[PERMISSION_FLAG_TYPES],
);
if (unknownFlagRows.length > 0) {
const unknownFlags = unknownFlagRows.map((row) => row.flag).join(', ');
throw new Error(
`Cannot migrate: rolePermissionFlag rows reference unknown flag value(s): ${unknownFlags}`,
);
}
for (const definition of STANDARD_PERMISSION_FLAG_DEFINITIONS) {
await dataSource.query(
`INSERT INTO "core"."permissionFlag" (
"id",
"workspaceId",
"applicationId",
"universalIdentifier",
"key",
"label",
"description",
"icon",
"permissionType",
"createdAt",
"updatedAt"View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- The thrown message lists the offending flag values — inspect them: SELECT DISTINCT flag FROM core.rolePermissionFlag WHERE flag NOT IN (<current enum>).
- If a flag was legitimately renamed, update the rows to the new canonical value before re-running.
- If a flag is genuinely obsolete and its grants should be dropped, delete or map those rolePermissionFlag rows intentionally, then re-run the slow migration.
- Ensure the deployed twenty-shared/constants matches the release expected by the DB state.
Example fix
-- before: rows reference a removed flag, migration refuses -- flag = 'CAN_MANAGE_OLD_FEATURE' -- fix: map to the canonical flag (or delete if obsolete), then re-run: -- UPDATE core.rolePermissionFlag -- SET flag = 'CAN_MANAGE_NEW_FEATURE' -- WHERE flag = 'CAN_MANAGE_OLD_FEATURE';
Defensive patterns
Strategy: validation
Validate before calling
// Run the same guard query before invoking the slow migration:
const unknown = await dataSource.query(
`SELECT DISTINCT "flag" FROM "core"."rolePermissionFlag" WHERE "flag" <> ALL($1::varchar[])`,
[Object.values(PermissionFlagType)],
);
if (unknown.length > 0) { /* map or delete offending flags before migrating */ } Type guard
import { PermissionFlagType } from 'twenty-shared/constants';
const isKnownPermissionFlag = (flag: string): flag is PermissionFlagType =>
(Object.values(PermissionFlagType) as string[]).includes(flag); Prevention
- Keep rolePermissionFlag.flag values aligned with the PermissionFlagType enum of the deployed release.
- On downgrade, map or remove flags that the older release does not know before running the slow migration.
- Never insert custom flag values from forks/plugins without adding them to the enum.
When it happens
Trigger: A workspace/instance has rolePermissionFlag rows with a 'flag' value that is not a member of the PermissionFlagType enum in the deployed code — e.g. a flag renamed/removed between releases, or rows inserted by a fork/plugin. The migration halts before the INSERT/UPDATE so no grants are lost.
Common situations: Downgrading to a release that removed a permission flag; running a fork that added custom flags not present in upstream; stale rows from a feature-flag experiment; mismatch between the deployed twenty-shared/constants version and the DB contents.
Related errors
- Failed to update search command menu item labels for workspa
- Failed to create CallRecording standard objects for workspac
- Failed to add Send Email record-selection commands for works
- Failed to backfill standard skills for workspace ${workspace
- Failed to fix merge command menu item expression for workspa
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/74e320ee855300ce.
Report an issue: GitHub.