{"record":{"id":"de95801c3ea341b1","repo":"paperclipai/paperclip","slug":"feature-disabled","errorCode":"FEATURE_DISABLED","errorMessage":"Conference Room Chat is not enabled","messagePattern":"Conference Room Chat is not enabled","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"server/src/routes/board-chat.ts","lineNumber":105,"sourceCode":"    } catch {\n      return (\n        \"You are a board-level assistant helping a human manage their AI-agent \" +\n        \"company through Paperclip. Help them create companies, hire agents, \" +\n        \"approve tasks, and monitor their organization. Be conversational, \" +\n        \"strategic, and concise.\"\n      );\n    }\n  }\n\n  router.post(\"/board/chat/stream\", async (req, res) => {\n    // Conference Room Chat is an experimental surface (PAP-136/PAP-137): the\n    // API is gated alongside the UI so the endpoint is inert while the flag\n    // is off, not just hidden.\n    const experimental = await instanceSettingsService(db).getExperimental();\n    if (experimental.enableConferenceRoomChat !== true) {\n      res.status(403).json({\n        error: \"Conference Room Chat is not enabled\",\n        code: \"FEATURE_DISABLED\",\n      });\n      return;\n    }\n\n    // The relay spawns the operator's local `claude` CLI with permissions\n    // skipped (it must run headless), so it is only safe where the requester\n    // IS the machine operator: local_trusted is loopback-only single-operator\n    // by construction (see server/src/index.ts boot guards). Refuse everywhere\n    // else rather than lending the server's shell to remote users.\n    if (opts.deploymentMode !== \"local_trusted\") {\n      res.status(403).json({\n        error: \"Board chat is only available on local single-operator instances\",\n        code: \"DEPLOYMENT_MODE_UNSUPPORTED\",\n      });\n      return;\n    }\n\n    const { companyId, message, taskId } = req.body as {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/server/src/routes/board-chat.ts#L87-L123","documentation":"Returned as HTTP 403 (code FEATURE_DISABLED) by POST /api/board/chat/stream when instanceSettings.getExperimental().enableConferenceRoomChat is not exactly true. The endpoint is intentionally inert (not just UI-hidden) while the experimental flag is off, because the relay spawns the operator's local claude CLI headless, which is unsafe to expose unless the feature is explicitly enabled.","triggerScenarios":"Calling /api/board/chat/stream on an instance where the enableConferenceRoomChat experimental setting is unset/false; common right after install, in CI, or before an operator has opted into the experiment.","commonSituations":"New deployment that has not enabled Conference Room Chat; UI calling the endpoint before the operator toggled the flag; a non-local_trusted deployment where the operator forgot the flag is also gated by deployment mode (the next guard returns DEPLOYMENT_MODE_UNSUPPORTED).","solutions":["Enable the experiment in instance settings: set experimental.enableConferenceRoomChat = true (and run on local_trusted deployment mode, which the next guard requires).","If you did not intend to use board chat, stop calling /api/board/chat/stream; the UI should hide the surface when the flag is off.","Confirm the instance-settings write took effect by re-reading getExperimental() before retrying."],"exampleFix":"// before\n// experimental.enableConferenceRoomChat is unset\n\n// after\nawait instanceSettingsService(db).update({\n  experimental: { enableConferenceRoomChat: true },\n});","handlingStrategy":"validation","validationCode":"async function boardChatEnabled(db: DB): Promise<boolean> {\n  const exp = await instanceSettingsService(db).getExperimental();\n  return exp.enableConferenceRoomChat === true;\n}\n// UI: hide the surface and do not POST /api/board/chat/stream unless this returns true.","typeGuard":"function isBoardChatEnabled(\n  exp: { enableConferenceRoomChat?: unknown },\n): exp is { enableConferenceRoomChat: true } {\n  return exp.enableConferenceRoomChat === true;\n}","tryCatchPattern":"try {\n  await fetch('/api/board/chat/stream', { ... });\n} catch (err) {\n  // Note: this returns 403, not a throw; handle in response parsing:\n  if (res.status === 403 && body.code === 'FEATURE_DISABLED') {\n    showFlagDisabledNotice();\n    return;\n  }\n}","preventionTips":["Gate the UI surface on getExperimental().enableConferenceRoomChat so the call is never made while disabled.","Remember the endpoint also requires deploymentMode === 'local_trusted'."],"tags":["feature-flag","board-chat","experimental","http-403","config"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}