twentyhq/twenty · critical · Error

Failed to gate default command menu items by permission flag

Error message

Failed to gate default command menu items by permission flag for workspace ${workspaceId}

What it means

Thrown by the 2.8 workspace upgrade command that gates default command menu items behind a permission flag. It fires only after the command has already attempted a legacy workspace migration via workspaceMigrationValidateBuildAndRunService.validateBuildAndRunLegacyWorkspaceMigration, and that service returned status === 'fail'. The migration service validates, builds, and runs flat-entity operations (here: commandMenuItem updates); a failure means the operation set did not pass validation or could not be built into a runnable migration for that workspace.

Source

Thrown at packages/twenty-server/src/database/commands/upgrade-version-command/2-8/2-8-workspace-command-1798100010000-gate-default-command-menu-items-by-permission-flag.command.ts:150

          allFlatEntityOperationByMetadataName: {
            commandMenuItem: {
              flatEntityToCreate: [],
              flatEntityToDelete: [],
              flatEntityToUpdate: itemsToUpdate,
            },
          },
          workspaceId,
          applicationUniversalIdentifier:
            twentyStandardFlatApplication.universalIdentifier,
        },
      );

    if (validateAndBuildResult.status === 'fail') {
      this.logger.error(
        `Failed to update command menu item availability expressions:\n${JSON.stringify(validateAndBuildResult, null, 2)}`,
      );

      throw new Error(
        `Failed to gate default command menu items by permission flag for workspace ${workspaceId}`,
      );
    }

    this.logger.log(
      `Successfully updated ${itemsToUpdate.length} command menu item availability expression(s) for workspace ${workspaceId}`,
    );
  }
}

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Read the preceding log line — `Failed to update command menu item availability expressions:` followed by the JSON dump — to get the exact validation/build error from the migration service.
  2. Inspect that workspace's commandMenuItem table (schema: the workspace's core schema) for rows missing fields the build expects, or rows whose id/key no longer match the standard command definitions.
  3. Re-run the upgrade command for the single failing workspace with --verbose to capture the full migration build trace, then fix the offending metadata row directly.
  4. If the workspace metadata is irreparably drifted, restore from backup or reset the workspace metadata to the standard set before re-running the upgrade.
  5. Verify the twentyStandardFlatApplication.universalIdentifier constant matches what the workspace was originally provisioned with.

Example fix

// before — throw with only workspaceId context, root cause only in a separate log line above
if (validateAndBuildResult.status === 'fail') {
  this.logger.error(`Failed to update...:\n${JSON.stringify(validateAndBuildResult, null, 2)}`);
  throw new Error(`Failed to gate default command menu items by permission flag for workspace ${workspaceId}`);
}
// after — embed the structured failure detail in the thrown message so operators see root cause in stack traces
type MigrationFail = { status: 'fail'; error?: string; message?: string; details?: unknown };
const fail = validateAndBuildResult as MigrationFail;
throw new Error(
  `Failed to gate default command menu items by permission flag for workspace ${workspaceId}: ` +
  `${fail.error ?? fail.message ?? 'see preceding log for full validateAndBuildResult'}`,
);
Defensive patterns

Strategy: try-catch

Validate before calling

// Before invoking the upgrade runner for a workspace, sanity-check the commandMenuItem rows the command will touch.
// (Read-only check; run in the same transaction scope the upgrade uses.)
const items = await dataSource.query(
  `SELECT id, "key", "conditionalAvailabilityExpression" FROM ${workspaceSchema}.commandMenuItem WHERE "key" = ANY($1)`,
  [requiredKeys],
);
if (items.length < requiredKeys.length) {
  const missing = requiredKeys.filter(k => !items.some(i => i.key === k));
  throw new Error(`commandMenuItem rows missing for keys: ${missing.join(', ')}`);
}

Type guard

// Narrow the migration result so the fail branch is statically known.
type ValidateBuildAndRunResult =
  | { status: 'success' }
  | { status: 'fail'; error?: string; message?: string; details?: unknown };
function isMigrationFail(r: ValidateBuildAndRunResult): r is Extract<ValidateBuildAndRunResult, { status: 'fail' }> {
  return r.status === 'fail';
}

Try / catch

try {
  await upgradeCommand.run();
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Failed to gate default command menu items')) {
    // log workspaceId, mark the workspace upgrade as failed-but-resumable, continue other workspaces
    logger.error({ workspaceId, err: err.message }, 'command-menu gate failed; workspace skipped');
    failedWorkspaceIds.push(workspaceId);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Running the 2.8 upgrade (`database:migrate:prod` or the upgrade runner iterating workspaces) against a workspace whose commandMenuItem records are in an unexpected state — e.g. an item referenced by the command no longer exists, a flat-entity field is missing/invalid, or the applicationUniversalIdentifier for twentyStandardFlatApplication does not match the workspace's stored metadata. Also triggered when the underlying migration build throws or the DB transaction aborts mid-run.

Common situations: Upgrading a self-hosted instance that skipped intermediate versions (so its metadata schema is partially drifted), running the upgrade against a workspace with manually-edited or corrupted command menu item metadata, or a partial previous run that left commandMenuItem rows half-updated. The logged JSON.stringify(validateAndBuildResult) immediately before the throw contains the precise failure reason.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/2272c01331225b61. Report an issue: GitHub.