{"record":{"id":"2272c01331225b61","repo":"twentyhq/twenty","slug":"failed-to-gate-default-command-menu-items-by-permi","errorCode":null,"errorMessage":"Failed to gate default command menu items by permission flag for workspace ${workspaceId}","messagePattern":"Failed to gate default command menu items by permission flag for workspace (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"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","lineNumber":150,"sourceCode":"          allFlatEntityOperationByMetadataName: {\n            commandMenuItem: {\n              flatEntityToCreate: [],\n              flatEntityToDelete: [],\n              flatEntityToUpdate: itemsToUpdate,\n            },\n          },\n          workspaceId,\n          applicationUniversalIdentifier:\n            twentyStandardFlatApplication.universalIdentifier,\n        },\n      );\n\n    if (validateAndBuildResult.status === 'fail') {\n      this.logger.error(\n        `Failed to update command menu item availability expressions:\\n${JSON.stringify(validateAndBuildResult, null, 2)}`,\n      );\n\n      throw new Error(\n        `Failed to gate default command menu items by permission flag for workspace ${workspaceId}`,\n      );\n    }\n\n    this.logger.log(\n      `Successfully updated ${itemsToUpdate.length} command menu item availability expression(s) for workspace ${workspaceId}`,\n    );\n  }\n}\n","sourceCodeStart":132,"sourceCodeEnd":160,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/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#L132-L160","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","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.","If the workspace metadata is irreparably drifted, restore from backup or reset the workspace metadata to the standard set before re-running the upgrade.","Verify the twentyStandardFlatApplication.universalIdentifier constant matches what the workspace was originally provisioned with."],"exampleFix":"// before — throw with only workspaceId context, root cause only in a separate log line above\nif (validateAndBuildResult.status === 'fail') {\n  this.logger.error(`Failed to update...:\\n${JSON.stringify(validateAndBuildResult, null, 2)}`);\n  throw new Error(`Failed to gate default command menu items by permission flag for workspace ${workspaceId}`);\n}\n// after — embed the structured failure detail in the thrown message so operators see root cause in stack traces\ntype MigrationFail = { status: 'fail'; error?: string; message?: string; details?: unknown };\nconst fail = validateAndBuildResult as MigrationFail;\nthrow new Error(\n  `Failed to gate default command menu items by permission flag for workspace ${workspaceId}: ` +\n  `${fail.error ?? fail.message ?? 'see preceding log for full validateAndBuildResult'}`,\n);","handlingStrategy":"try-catch","validationCode":"// Before invoking the upgrade runner for a workspace, sanity-check the commandMenuItem rows the command will touch.\n// (Read-only check; run in the same transaction scope the upgrade uses.)\nconst items = await dataSource.query(\n  `SELECT id, \"key\", \"conditionalAvailabilityExpression\" FROM ${workspaceSchema}.commandMenuItem WHERE \"key\" = ANY($1)`,\n  [requiredKeys],\n);\nif (items.length < requiredKeys.length) {\n  const missing = requiredKeys.filter(k => !items.some(i => i.key === k));\n  throw new Error(`commandMenuItem rows missing for keys: ${missing.join(', ')}`);\n}","typeGuard":"// Narrow the migration result so the fail branch is statically known.\ntype ValidateBuildAndRunResult =\n  | { status: 'success' }\n  | { status: 'fail'; error?: string; message?: string; details?: unknown };\nfunction isMigrationFail(r: ValidateBuildAndRunResult): r is Extract<ValidateBuildAndRunResult, { status: 'fail' }> {\n  return r.status === 'fail';\n}","tryCatchPattern":"try {\n  await upgradeCommand.run();\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Failed to gate default command menu items')) {\n    // log workspaceId, mark the workspace upgrade as failed-but-resumable, continue other workspaces\n    logger.error({ workspaceId, err: err.message }, 'command-menu gate failed; workspace skipped');\n    failedWorkspaceIds.push(workspaceId);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Run upgrade commands against a restored backup of production in staging first, so metadata drift surfaces before prod.","Keep instance commands in sequence — do not skip versions between major upgrades.","Capture the preceding structured log line (validateAndBuildResult JSON) in your upgrade runner's error reporter so root cause travels with the alert."],"tags":["database-migration","workspace-upgrade","command-menu","nestjs"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}