strapi/strapi · error · Error
You can't register new conditions outside the bootstrap func
Error message
You can't register new conditions outside the bootstrap function.
What it means
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').
Source
Thrown at packages/core/core/src/services/content-api/permissions/providers/condition.ts:16
import { providerFactory } from '@strapi/utils';
export interface Condition {
name: string;
[key: string]: unknown;
}
export default (options = {}) => {
const provider = providerFactory(options);
return {
...provider,
async register(condition: Condition) {
if (strapi.isLoaded) {
throw new Error(`You can't register new conditions outside the bootstrap function.`);
}
return provider.register(condition.name, condition);
},
};
};
View on GitHub (pinned to 4a4101264d)
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.
Example fix
// before (throws after load)
async function myService() {
strapi.conditionProvider.register({ name: 'is-owner', handler: ... });
}
// after (runs in bootstrap)
module.exports = {
async bootstrap({ strapi }) {
const { conditionProvider } = strapi.service('admin::permission');
conditionProvider.register({ name: 'is-owner', handler: ({ user }) => ({ id: user.id }) });
},
}; Defensive patterns
Strategy: validation
Validate before calling
// Only register conditions while strapi is still loading
if (!strapi.isLoaded) {
strapi.conditionProvider.register({ name: 'is-owner', handler });
} else {
strapi.log.warn('Skipped condition registration: strapi already loaded');
} Type guard
const isCondition = (c: unknown): c is { name: string; [k: string]: unknown } =>
typeof c === 'object' && c !== null && typeof (c as any).name === 'string'; Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- You can't register new actions outside the bootstrap functio
- Super admin permissions can't be edited.
- role.notFound
- Invalid key ${key} at ${path}
- Invalid key ${key}
AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12).
Data as JSON: /api/errors/4fab6e625fba45b6.
Report an issue: GitHub.