{"record":{"id":"4fab6e625fba45b6","repo":"strapi/strapi","slug":"you-can-t-register-new-conditions-outside-the-boot","errorCode":null,"errorMessage":"You can't register new conditions outside the bootstrap function.","messagePattern":"You can't register new conditions outside the bootstrap function\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/core/src/services/content-api/permissions/providers/condition.ts","lineNumber":16,"sourceCode":"import { providerFactory } from '@strapi/utils';\n\nexport interface Condition {\n  name: string;\n  [key: string]: unknown;\n}\n\nexport default (options = {}) => {\n  const provider = providerFactory(options);\n\n  return {\n    ...provider,\n\n    async register(condition: Condition) {\n      if (strapi.isLoaded) {\n        throw new Error(`You can't register new conditions outside the bootstrap function.`);\n      }\n\n      return provider.register(condition.name, condition);\n    },\n  };\n};\n","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/strapi/strapi/blob/4a4101264d7098754df36e85fa629fd2f2349d8c/packages/core/core/src/services/content-api/permissions/providers/condition.ts#L1-L23","documentation":"Strapi freezes the permissions condition registry once the bootstrap phase completes. The condition provider's register() checks strapi.isLoaded and refuses new conditions afterwards, because conditions must be available before any request is authorized. Registering later would leave already-cached permission checks inconsistent. Conditions extend RBAC with attribute-level rules (e.g. 'author equals me').","triggerScenarios":"Calling strapi.conditionProvider.register({ name, plugin, handler }) (or the content-api permissions condition service register) from inside a controller, a service method, or any code path that runs after strapi.start()/bootstrap has finished — i.e. when strapi.isLoaded === true.","commonSituations":"Moving a condition registration out of ./src/index.js bootstrap() into a service; registering conditions lazily on first request; a plugin that registers conditions in register() but the host app calls them late; upgrading and not realizing bootstrap already ran in tests.","solutions":["Move the conditionProvider.register(...) call into the bootstrap function in ./src/index.js (or the plugin's bootstrap) so it runs before strapi.isLoaded becomes true.","If registering from a plugin, put it in the plugin's bootstrap({ strapi }) hook, not in register() callbacks that fire after load.","In tests, register conditions before calling await strapi.start() or use a fresh Strapi instance per test.","Confirm the call site runs synchronously during bootstrap, not in a deferred setTimeout/promise that resolves after load."],"exampleFix":"// before (throws after load)\nasync function myService() {\n  strapi.conditionProvider.register({ name: 'is-owner', handler: ... });\n}\n\n// after (runs in bootstrap)\nmodule.exports = {\n  async bootstrap({ strapi }) {\n    const { conditionProvider } = strapi.service('admin::permission');\n    conditionProvider.register({ name: 'is-owner', handler: ({ user }) => ({ id: user.id }) });\n  },\n};","handlingStrategy":"validation","validationCode":"// Only register conditions while strapi is still loading\nif (!strapi.isLoaded) {\n  strapi.conditionProvider.register({ name: 'is-owner', handler });\n} else {\n  strapi.log.warn('Skipped condition registration: strapi already loaded');\n}","typeGuard":"const isCondition = (c: unknown): c is { name: string; [k: string]: unknown } =>\n  typeof c === 'object' && c !== null && typeof (c as any).name === 'string';","tryCatchPattern":null,"preventionTips":["Centralize all condition registrations in a single bootstrap function.","Add a test that asserts conditions are registered before strapi.start() resolves.","Never call register from controllers, services, or request-scoped code."],"tags":["permissions","rbac","bootstrap","lifecycle","conditions"],"backgroundTag":null,"analyzedSha":"4a4101264d7098754df36e85fa629fd2f2349d8c","analyzedAt":"2026-08-12T12:47:50.760Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}