{"record":{"id":"e3c401559cbd3bbb","repo":"koala73/worldmonitor","slug":"company-monitoring-account-inactive-e3c401","errorCode":null,"errorMessage":"COMPANY_MONITORING_ACCOUNT_INACTIVE","messagePattern":"COMPANY_MONITORING_ACCOUNT_INACTIVE","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/companyMonitoring/orchestration.ts","lineNumber":285,"sourceCode":"  await ctx.db.patch(account._id, {\n    nextExaScanDueAt,\n    nextXScanDueAt,\n    updatedAt: Date.now(),\n  });\n}\n\nasync function scheduleAccountWorkHandler(\n  ctx: MutationCtx,\n  args: { ownerAccountId: string; source: Source; companyIds: string[] },\n  mode: \"strict\" | \"missing_only\" = \"strict\",\n) {\n  const now = Date.now();\n  const account = await ctx.db\n    .query(\"companyMonitoringAccounts\")\n    .withIndex(\"by_logicalAccountId\", (q) => q.eq(\"logicalAccountId\", args.ownerAccountId))\n    .unique();\n  if (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n    throw new ConvexError(\"COMPANY_MONITORING_ACCOUNT_INACTIVE\");\n  }\n  requireProviderClaimPolicy(account, args.source);\n\n  const requestedCompanyIds = [...new Set(args.companyIds)].sort();\n  if (\n    requestedCompanyIds.length === 0 ||\n    requestedCompanyIds.length > COMPANY_MONITORING_SCAN_COHORT_LIMIT\n  ) {\n    throw new ConvexError(\"INVALID_COMPANY_MONITORING_COHORT\");\n  }\n\n  const rows = await Promise.all(requestedCompanyIds.map(async (companyId) => {\n    const [company, obligation] = await Promise.all([\n      ctx.db\n        .query(\"companyMonitoringCompanies\")\n        .withIndex(\"by_account_companyId\", (q) =>\n          q.eq(\"ownerAccountId\", args.ownerAccountId).eq(\"companyId\", companyId),\n        )","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/companyMonitoring/orchestration.ts#L267-L303","documentation":"Thrown by scheduleAccountWorkHandler when the owner account is missing, its lifecycle is not 'entitled', or it has a terminalReason. Scan scheduling is only permitted for fully-entitled, non-terminal accounts, so this guard stops new work from being queued for accounts that can no longer consume it.","triggerScenarios":"Calling ensureAccountWork/scheduleAccountWork for an ownerAccountId whose account row is gone, suspended/cancelled (lifecycle != 'entitled'), or marked terminal.","commonSituations":"A queued follow-up mutation firing after the account was suspended; account teardown did not drain pending scheduler callbacks; billing lapse marking the account terminal.","solutions":["Before scheduling, confirm the account is lifecycle 'entitled' with no terminalReason.","Ensure account teardown cancels queued scheduler callbacks (runAfter) for that account.","Treat this error as terminal for the scheduling path: do not retry blindly."],"exampleFix":"// before\nawait queueAccountSourceWork(ctx, ownerAccountId, source, companyIds);\n\n// after\nconst account = await fetchAccount(ownerAccountId);\nif (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n  return { status: \"inactive\" };\n}\nawait queueAccountSourceWork(ctx, ownerAccountId, source, companyIds);","handlingStrategy":"validation","validationCode":"const account = await fetchAccount(ownerAccountId);\nif (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n  return { status: \"inactive\" };\n}","typeGuard":"function isSchedulableAccount(a: { lifecycle: string; terminalReason?: string } | null): boolean {\n  return Boolean(a) && a.lifecycle === \"entitled\" && !a.terminalReason;\n}","tryCatchPattern":null,"preventionTips":["Confirm entitlement before queueing account work.","Drain queued callbacks for an account during teardown."],"tags":["convex","authorization","account","scheduling","company-monitoring"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}