{"record":{"id":"a266871940fcf054","repo":"stablyai/orca","slug":"creating-review-files-is-disabled","errorCode":null,"errorMessage":"creating review files is disabled","messagePattern":"creating review files is disabled","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/main/ipc/diagnostics.ts","lineNumber":221,"sourceCode":"  })\n  return result.response === 0\n}\n\nexport function registerDiagnosticsHandlers(): void {\n  ipcMain.handle('diagnostics:getStatus', (): DiagnosticsStatus => {\n    return getDiagnosticsStatus()\n  })\n\n  ipcMain.handle(\n    'diagnostics:collectBundle',\n    (_event, lookbackMinutesIn: unknown): DiagnosticsBundlePreview => {\n      // Consent gate: main is the consent enforcement boundary; the\n      // renderer-side button-hide is UX, not security. A compromised or\n      // malicious renderer must not be able to assemble a bundle when the\n      // user has disabled diagnostic-bundle collection in Settings → Privacy.\n      const status = getDiagnosticsStatus()\n      if (!status.bundleEnabled) {\n        throw new Error('creating review files is disabled')\n      }\n      // Renderer-controlled input → narrow at the boundary. The default\n      // (DEFAULT_LOOKBACK_MINUTES in bundle.ts) is fine for the common\n      // \"last 30 minutes\" case the Privacy pane button triggers.\n      const lookbackMinutes =\n        typeof lookbackMinutesIn === 'number' && Number.isFinite(lookbackMinutesIn)\n          ? Math.max(1, Math.min(30 * 24 * 60, Math.floor(lookbackMinutesIn)))\n          : undefined\n      const bundle = collectDiagnosticBundle({\n        appVersion: app.getVersion(),\n        platform: osPlatform(),\n        arch: osArch(),\n        osRelease: osRelease(),\n        orcaChannel: resolveDiagnosticOrcaChannel(),\n        ...(lookbackMinutes !== undefined ? { lookbackMinutes } : {})\n      })\n      rememberBundle(bundle)\n      return toBundlePreview(bundle)","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/diagnostics.ts#L203-L239","documentation":"Thrown by the `diagnostics:collectBundle` IPC handler when `getDiagnosticsStatus().bundleEnabled` is false. The main process is the consent enforcement boundary: the renderer-side button hiding is only UX, so a compromised or malicious renderer cannot assemble a diagnostic bundle once the user has disabled 'Create diagnostic bundles' in Settings -> Privacy. It is an intentional, user-triggered refusal, not a crash.","triggerScenarios":"Calling `ipcRenderer.invoke('diagnostics:collectBundle', lookbackMinutes)` while Settings -> Privacy -> diagnostic-bundle collection is disabled, so `getDiagnosticsStatus()` returns `{ bundleEnabled: false }`.","commonSituations":"User toggled the Privacy setting off; stale renderer UI that did not react to the setting change and left the collect button enabled; a script or test driving IPC directly without consulting status first.","solutions":["Invoke `diagnostics:getStatus` first and only call `diagnostics:collectBundle` when `status.bundleEnabled === true`.","Re-enable diagnostic-bundle collection in Settings -> Privacy.","Subscribe the renderer to status changes so the collect button is disabled/hidden in lockstep with the setting."],"exampleFix":"// before\nawait ipcRenderer.invoke('diagnostics:collectBundle', 30)\n\n// after\nconst status = await ipcRenderer.invoke('diagnostics:getStatus')\nif (!status.bundleEnabled) {\n  // surface 'Enable diagnostic bundles in Settings -> Privacy' to the user\n  return\n}\nawait ipcRenderer.invoke('diagnostics:collectBundle', 30)","handlingStrategy":"validation","validationCode":"// Before invoking collectBundle, confirm consent is currently enabled.\nconst status = await ipcRenderer.invoke('diagnostics:getStatus')\nif (!status.bundleEnabled) {\n  // do not call collectBundle; prompt user to enable in Settings -> Privacy\n  return\n}\nawait ipcRenderer.invoke('diagnostics:collectBundle', lookbackMinutes)","typeGuard":"function isBundleEnabledStatus(s: unknown): s is { bundleEnabled: true } {\n  return typeof s === 'object' && s !== null && (s as any).bundleEnabled === true\n}","tryCatchPattern":"try {\n  await ipcRenderer.invoke('diagnostics:collectBundle', mins)\n} catch (e) {\n  if (e instanceof Error && e.message === 'creating review files is disabled') {\n    showEnableDiagnosticsNotice()\n    return\n  }\n  throw e\n}","preventionTips":["Always read diagnostics:getStatus immediately before any collect/upload call; never cache consent across user actions.","Bind the collect button's disabled state to a live subscription of diagnostics status.","Treat consent as able to change at any time; design the flow to abort cleanly on refusal."],"tags":["diagnostics","consent","privacy","ipc","security-boundary"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}