{"record":{"id":"fbf152b153c9ee71","repo":"koala73/worldmonitor","slug":"removed-company-is-terminal","errorCode":null,"errorMessage":"REMOVED_COMPANY_IS_TERMINAL","messagePattern":"REMOVED_COMPANY_IS_TERMINAL","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/companyMonitoring/companies.ts","lineNumber":361,"sourceCode":"export const setCompanyStateForOwner = internalMutation({\n  args: {\n    ownerUserId: v.string(),\n    companyId: v.string(),\n    state: v.union(v.literal(\"active\"), v.literal(\"paused\"), v.literal(\"removed\")),\n  },\n  handler: async (ctx, args) => {\n    const account = await requireActiveAccount(ctx, args.ownerUserId);\n    const company = await ctx.db\n      .query(\"companyMonitoringCompanies\")\n      .withIndex(\"by_account_companyId\", (q) =>\n        q.eq(\"ownerAccountId\", account.logicalAccountId).eq(\"companyId\", args.companyId),\n      )\n      .unique();\n    if (!company) throw new ConvexError(\"NOT_FOUND\");\n    try {\n      assertLifecycleTransition(\"company\", company.lifecycle, args.state);\n    } catch {\n      throw new ConvexError(\"REMOVED_COMPANY_IS_TERMINAL\");\n    }\n    if (company.lifecycle === \"removed\") {\n      if (args.state === \"removed\") return { status: \"already_removed\", companyId: company.companyId };\n      throw new ConvexError(\"REMOVED_COMPANY_IS_TERMINAL\");\n    }\n    if (company.lifecycle === args.state) return { status: \"unchanged\", companyId: company.companyId };\n\n    const now = Date.now();\n    if (args.state !== \"removed\") {\n      if (args.state === \"paused\") {\n        await cancelCompanyScanWork(ctx, {\n          ownerAccountId: account.logicalAccountId,\n          companyId: company.companyId,\n          reason: \"superseded\",\n        });\n      }\n      await ctx.db.patch(company._id, {\n        lifecycle: args.state,","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/companyMonitoring/companies.ts#L343-L379","documentation":"Thrown by setCompanyStateForOwner when assertLifecycleTransition('company', currentLifecycle, targetState) throws. This is the generic lifecycle-transition guard: it rejects any transition the company state machine does not permit (e.g., paused->removed is allowed, but invalid combinations are not). The catch swallows the specific reason and rethrows this terminal-styled error.","triggerScenarios":"Requesting a state transition the company lifecycle machine disallows, e.g. an illegal direct jump not whitelisted by assertLifecycleTransition.","commonSituations":"Calling setState with a state value outside active|paused|removed (caught earlier by the validator) or a disallowed source->target pairing; a stale client sending a transition that newer contract rules forbid.","solutions":["Check assertLifecycleTransition's allowed transitions in shared/company-monitoring-contract before calling.","Only request transitions to active, paused, or removed from their documented allowed source states.","Re-read the company's current lifecycle and compute a legal target from it."],"exampleFix":"// before\nawait setState({ ownerUserId, companyId, state: targetState }); // may be illegal\n\n// after\nconst company = await fetchCompany(companyId);\nif (!isLegalCompanyTransition(company.lifecycle, targetState)) {\n  throw new UserError(`Cannot transition ${company.lifecycle} -> ${targetState}`);\n}\nawait setState({ ownerUserId, companyId, state: targetState });","handlingStrategy":"validation","validationCode":"import { assertLifecycleTransition } from \"shared/company-monitoring-contract\";\ntry {\n  assertLifecycleTransition(\"company\", current.lifecycle, targetState);\n} catch {\n  throw new UserError(`Illegal transition ${current.lifecycle} -> ${targetState}`);\n}","typeGuard":"function isLegalCompanyTransition(from: string, to: \"active\"|\"paused\"|\"removed\"): boolean {\n  try { assertLifecycleTransition(\"company\", from, to); return true; } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Consult the shared lifecycle transition table before requesting a state change.","Re-read the current lifecycle and compute a legal target from it."],"tags":["convex","state-machine","lifecycle","company-monitoring"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}