{"record":{"id":"207743aea6306b29","repo":"Budibase/budibase","slug":"configuration-cannot-be-deactivated-while-sso-is-e","errorCode":null,"errorMessage":"Configuration cannot be deactivated while SSO is enforced","messagePattern":"Configuration cannot be deactivated while SSO is enforced","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/worker/src/api/controllers/global/configs.ts","lineNumber":224,"sourceCode":"    config.faviconUrl = existingConfig.faviconUrl\n    config.faviconUrlEtag = existingConfig.faviconUrlEtag\n  }\n}\n\nasync function verifySSOConfig(type: SSOConfigType, config: SSOConfig) {\n  const settings = await configs.getSettingsConfig()\n  if (settings.isSSOEnforced && !config.activated) {\n    // config is being saved as deactivated\n    // ensure there is at least one other activated sso config\n    const ssoConfigs = await getSSOConfigs()\n\n    // overwrite the config being updated\n    // to reflect the desired state\n    ssoConfigs[type] = config\n\n    const activated = await hasActivatedConfig(ssoConfigs)\n    if (!activated) {\n      throw new Error(\n        \"Configuration cannot be deactivated while SSO is enforced\"\n      )\n    }\n  }\n}\n\nasync function processGoogleConfig(\n  config: GoogleInnerConfig,\n  existing?: GoogleInnerConfig\n) {\n  await verifySSOConfig(ConfigType.GOOGLE, config)\n\n  if (existing && config.clientSecret === PASSWORD_REPLACEMENT) {\n    config.clientSecret = existing.clientSecret\n  }\n}\n\nasync function processOIDCConfig(config: OIDCConfigs, existing?: OIDCConfigs) {","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/configs.ts#L206-L242","documentation":"verifySSOConfig runs for Google/OIDC config saves. When SSO enforcement is active it re-checks hasActivatedConfig() over the merged set of SSO configs (including the update being applied). If the update would leave zero activated SSO configs, enforcement could not be satisfied, so the change is rejected.","triggerScenarios":"Saving a Google or OIDC config that deactivates the last activated SSO config (e.g. activated=false, or deleting/overwriting the sole active config) while isSSOEnforced is on.","commonSituations":"Admin edits an SSO provider and unchecks 'activated' not realizing it is the only active provider while SSO enforcement is enabled; tenant-wide lockout prevention kicks in.","solutions":["Activate another SSO config first, then deactivate this one","Temporarily disable SSO enforcement in settings before deactivating the provider","Keep the config activated and only update its credentials/endpoints","Check the payload: ensure activated is not accidentally set to false on the only active config"],"exampleFix":"// before\n{ \"type\": \"oidc\", \"config\": { \"activated\": false } } // last active provider\n// after\n// disable enforcement first, then deactivate the provider","handlingStrategy":"validation","validationCode":"const others = ssoConfigs.filter(c => c.type !== beingEdited.type)\nconst otherActivated = others.some(c => c.config?.activated)\nif (settings.isSSOEnforced && !otherActivated && !newConfig.activated) {\n  // block: this would leave no activated provider while SSO is enforced\n}","typeGuard":null,"tryCatchPattern":"try {\n  await configApi.save(ssoConfig)\n} catch (err) {\n  if (err.message.includes(\"Configuration cannot be deactivated while SSO is enforced\")) {\n    // disable enforcement first or activate a second provider\n  } else { throw err }\n}","preventionTips":["Never uncheck 'activated' on the sole active provider while enforcement is on","Deactivate enforcement in settings before removing the last SSO provider","Audit activated flags after every SSO config edit","Keep a second provider configured as a fallback"],"tags":["sso","oidc","google","configuration"],"backgroundTag":"sso-config-state-conflict","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}