{"record":{"id":"15b3e95a78dd0edb","repo":"koala73/worldmonitor","slug":"company-monitoring-account-inactive","errorCode":null,"errorMessage":"COMPANY_MONITORING_ACCOUNT_INACTIVE","messagePattern":"COMPANY_MONITORING_ACCOUNT_INACTIVE","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/companyMonitoring/evidence.ts","lineNumber":371,"sourceCode":"}\n\nexport async function ingestCompanyEvidenceForCompanyIds(\n  ctx: MutationCtx,\n  input: {\n    ownerAccountId: string;\n    companyIds: string[];\n    evidence: ProviderEvidence[];\n  },\n) {\n  if (input.evidence.length === 0 || input.evidence.length > MAX_INGESTION_ROWS) {\n    throw new ConvexError(\"COMPANY_MONITORING_EVIDENCE_BATCH_INVALID\");\n  }\n  const account = await ctx.db\n    .query(\"companyMonitoringAccounts\")\n    .withIndex(\"by_logicalAccountId\", (q) => q.eq(\"logicalAccountId\", input.ownerAccountId))\n    .unique();\n  if (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n    throw new ConvexError(\"COMPANY_MONITORING_ACCOUNT_INACTIVE\");\n  }\n  const subjects = await canonicalSubjects(ctx, input.ownerAccountId, input.companyIds);\n  const normalized = await normalizeCompanyEvidence({\n    ownerAccountId: input.ownerAccountId,\n    subjects,\n    evidence: input.evidence,\n    now: Date.now(),\n  });\n  if (normalized.evidence.length > MAX_EXPANDED_EVIDENCE_ROWS) {\n    throw new ConvexError(\"COMPANY_MONITORING_EVIDENCE_EXPANSION_INVALID\");\n  }\n  const now = Date.now();\n  const affected = new Map<string, Map<string, \"evidence_unavailable\" | undefined>>();\n  const rememberOccurrence = (\n    companyId: string,\n    occurrenceDedupeKey: string,\n    fallbackLossReason?: \"evidence_unavailable\",\n  ) => {","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/companyMonitoring/evidence.ts#L353-L389","documentation":"Thrown by ingestCompanyEvidenceForCompanyIds when the owner account row is missing, its lifecycle is not 'entitled', or it carries a terminalReason. Evidence ingestion is only permitted for fully-entitled accounts that have not been terminally deactivated.","triggerScenarios":"Ingesting evidence for an ownerAccountId whose companyMonitoringAccounts row has lifecycle other than 'entitled' (e.g., 'suspended', 'cancelled') or has a terminalReason set.","commonSituations":"Account suspended/cancelled between when a scan was scheduled and when the worker returns evidence; billing lapse setting terminalReason; worker retrying an old lease after account teardown.","solutions":["Before ingesting, verify the account is lifecycle 'entitled' with no terminalReason.","On this error, stop retrying work for the account and surface it for entitlement review.","Ensure account teardown cancels in-flight obligations before marking the account terminal."],"exampleFix":"// before\nawait ingest({ ownerAccountId, companyIds, evidence });\n\n// after\nconst account = await fetchAccount(ownerAccountId);\nif (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n  return { status: \"account_inactive\" };\n}\nawait ingest({ ownerAccountId, companyIds, evidence });","handlingStrategy":"validation","validationCode":"const account = await fetchAccount(ownerAccountId);\nif (!account || account.lifecycle !== \"entitled\" || account.terminalReason) {\n  return { status: \"account_inactive\" };\n}","typeGuard":"function isEntitledAccount(a: { lifecycle: string; terminalReason?: string } | null): boolean {\n  return Boolean(a) && a.lifecycle === \"entitled\" && !a.terminalReason;\n}","tryCatchPattern":null,"preventionTips":["Check entitlement before ingestion to avoid the round trip.","Stop retrying work for accounts that return inactive."],"tags":["convex","authorization","account","evidence","company-monitoring"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}