{"record":{"id":"903df36f1b29dc8a","repo":"toeverything/AFFiNE","slug":"action-forbidden-903df3","errorCode":"action_forbidden","errorMessage":"Copilot is disabled.","messagePattern":"Copilot is disabled\\.","errorType":"exception","errorClass":"ActionForbidden","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/availability.ts","lineNumber":6,"sourceCode":"import type { Config } from '../../base/config';\nimport { ActionForbidden } from '../../base/error/errors.gen';\n\nexport function assertCopilotEnabled(config: Config) {\n  if (!config.copilot.enabled) {\n    throw new ActionForbidden('Copilot is disabled.');\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":9,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/availability.ts#L1-L9","documentation":"Guard in the copilot plugin's availability helper: any copilot feature route rejects with ActionForbidden when config.copilot.enabled is false. It is the single choke point that keeps every AI endpoint unavailable until an operator flips the copilot.enabled config item, so hitting it means the feature flag is off on this deployment, not a per-user permission problem.","triggerScenarios":"Any copilot API/GQL call on a fresh self-hosted install (default is enabled: false); admin disabled the plugin via config; environment-specific config file missing the copilot section after migration; calling copilot endpoints from tooling before enabling the plugin.","commonSituations":"New self-hosted deployments where AI features are opt-in; CI suites exercising copilot endpoints without enabling the flag; config typos like copilot.enable that silently leave the default false.","solutions":["Set the copilot.enabled config item to true (e.g. AFFINE_COPILOT_ENABLED=true or the equivalent config source) and restart the server.","Verify with a config dump / diagnostics endpoint that copilot.enabled actually reads true after restart.","On the client, gate AI UI behind the server-advertised copilot availability so users never reach the endpoint."],"exampleFix":"# before (env)\nAFFINE_COPILOT_ENABLED=false\n\n# after\nAFFINE_COPILOT_ENABLED=true","handlingStrategy":"validation","validationCode":"// Client: check advertised availability before calling copilot APIs\nconst { copilotEnabled } = await client.getServerCapabilities();\nif (!copilotEnabled) disableAiFeatures();","typeGuard":null,"tryCatchPattern":"try {\n  await copilotApi.chat(...);\n} catch (e) {\n  if (e?.code === 'action_forbidden' && /copilot is disabled/i.test(e.message)) {\n    showSetupNotice('Ask the admin to enable copilot (AFFINE_COPILOT_ENABLED=true)');\n  } else throw e;\n}","preventionTips":["Enable the copilot plugin in config before deploying any AI-dependent feature.","Expose and consume a capability flag so clients never call disabled endpoints.","Verify config parsing (typo'd keys silently keep defaults) with a config dump after changes."],"tags":["copilot","feature-flag","config","authorization"],"backgroundTag":"feature-disabled-by-config","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}