koala73/worldmonitor · error · ConvexError

NOT_FOUND

NOT_FOUND

Error message

NOT_FOUND

What it means

Thrown by the company update mutation (convex/companyMonitoring/companies.ts:202) when no companyMonitoringCompanies row exists for ownerAccountId + companyId, or the row has lifecycle "removed", or is missing name or domicileCountry. Updates are only allowed on live, fully-formed companies.

Solutions

  1. Refresh the company list and confirm the id still exists with lifecycle active/paused before showing an edit form
  2. If the company was removed, create a new company instead of updating the dead id
  3. Verify you are calling with the correct ownerUserId (the account that owns the company)
  4. Handle NOT_FOUND by returning the user to the list view rather than retrying

Example fix

// before
await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch });

// after
const company = await api.companyMonitoring.getCompany({ ownerUserId, companyId });
if (!company || company.lifecycle === "removed") {
  router.replace("/companies"); // stale reference; reload list
  return;
}
await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch });
Defensive patterns

Strategy: validation

Validate before calling

const company = await api.companyMonitoring.getCompany({ ownerUserId, companyId });
const updatable = !!company && company.lifecycle !== "removed" && !!company.name && !!company.domicileCountry;
if (!updatable) {
  throw new UserFacingError("Company not found or no longer editable.");
}
await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch });

Type guard

function isEditableCompany(company: { lifecycle: string; name?: string; domicileCountry?: string } | null | undefined): company is { lifecycle: string; name: string; domicileCountry: string } {
  return !!company && company.lifecycle !== "removed" && typeof company.name === "string" && typeof company.domicileCountry === "string";
}

Try / catch

try {
  await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch });
} catch (err) {
  if (err instanceof ConvexError && err.data === "NOT_FOUND") {
    router.replace("/companies"); // stale reference — reload the list
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling update with a companyId from another account or a typo'd id; updating a company already removed (removal is terminal); updating a company whose required fields were somehow cleared; stale client state referencing a deleted company.

Common situations: Two admin sessions where one removed the company; client caches holding an old company list; id mix-ups between environments (staging id sent to production); import scripts re-running against already-removed rows.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21). Data as JSON: /api/errors/f874fde33a9f914d. Report an issue: GitHub.

Appendix: source

Thrown at convex/companyMonitoring/companies.ts:202

  },
});

export const updateCompanyForOwner = internalMutation({
  args: {
    ownerUserId: v.string(),
    companyId: v.string(),
    patch: companyPatchValidator,
  },
  handler: async (ctx, args) => {
    const account = await requireActiveAccount(ctx, args.ownerUserId);
    const company = await ctx.db
      .query("companyMonitoringCompanies")
      .withIndex("by_account_companyId", (q) =>
        q.eq("ownerAccountId", account.logicalAccountId).eq("companyId", args.companyId),
      )
      .unique();
    if (!company || company.lifecycle === "removed" || !company.name || !company.domicileCountry) {
      throw new ConvexError("NOT_FOUND");
    }
    const patch = args.patch;
    const addClaimInputs = patch.addClaims ?? [];
    const removeClaimInputs = patch.removeClaimIds ?? [];
    if (
      addClaimInputs.length > COMPANY_MONITORING_LIMITS.maxClaimsPerCompany ||
      removeClaimInputs.length > COMPANY_MONITORING_LIMITS.maxClaimsPerCompany
    ) {
      throw new ConvexError("INVALID_COMPANY_PATCH");
    }

    const hasName = Object.prototype.hasOwnProperty.call(patch, "name");
    const hasDomicile = Object.prototype.hasOwnProperty.call(patch, "domicileCountry");
    const hasCustomerReference = Object.prototype.hasOwnProperty.call(patch, "customerReference");
    const normalizedFields = normalizeMonitoredCompanyInput({
      name: hasName ? patch.name! : company.name,
      domicileCountry: hasDomicile ? patch.domicileCountry! : company.domicileCountry,
      customerReference: hasCustomerReference

View on GitHub (pinned to 7d06c8633d)