koala73/worldmonitor · error · ConvexError
COMPANY_MONITORING_EVIDENCE_EXPANSION_INVALID
Error message
COMPANY_MONITORING_EVIDENCE_EXPANSION_INVALID
What it means
Thrown by ingestCompanyEvidenceForCompanyIds when the normalized (expanded) evidence list exceeds MAX_EXPANDED_EVIDENCE_ROWS (625, i.e. 25 companies * 25 rows). normalizeCompanyEvidence can multiply the input because a single provider row may be routed to multiple subject companies; this guard rejects an over-broad expansion before any write so the receipt stays atomic within the scheduler budget.
Source
Thrown at convex/companyMonitoring/evidence.ts:381
if (input.evidence.length === 0 || input.evidence.length > MAX_INGESTION_ROWS) {
throw new ConvexError("COMPANY_MONITORING_EVIDENCE_BATCH_INVALID");
}
const account = await ctx.db
.query("companyMonitoringAccounts")
.withIndex("by_logicalAccountId", (q) => q.eq("logicalAccountId", input.ownerAccountId))
.unique();
if (!account || account.lifecycle !== "entitled" || account.terminalReason) {
throw new ConvexError("COMPANY_MONITORING_ACCOUNT_INACTIVE");
}
const subjects = await canonicalSubjects(ctx, input.ownerAccountId, input.companyIds);
const normalized = await normalizeCompanyEvidence({
ownerAccountId: input.ownerAccountId,
subjects,
evidence: input.evidence,
now: Date.now(),
});
if (normalized.evidence.length > MAX_EXPANDED_EVIDENCE_ROWS) {
throw new ConvexError("COMPANY_MONITORING_EVIDENCE_EXPANSION_INVALID");
}
const now = Date.now();
const affected = new Map<string, Map<string, "evidence_unavailable" | undefined>>();
const rememberOccurrence = (
companyId: string,
occurrenceDedupeKey: string,
fallbackLossReason?: "evidence_unavailable",
) => {
const occurrences = affected.get(companyId) ?? new Map();
if (!occurrences.has(occurrenceDedupeKey) || fallbackLossReason === undefined) {
occurrences.set(occurrenceDedupeKey, fallbackLossReason);
}
affected.set(companyId, occurrences);
};
for (const evidence of normalized.evidence) {
const existing = await ctx.db
.query("companyMonitoringEvidence")
.withIndex("by_account_company_locator", (q) =>View on GitHub (pinned to ffec79ac33)
Solutions
- Tighten the provider query/claim matching so fewer rows route to each company.
- Split the ingestion so each call covers fewer subjects or fewer evidence rows.
- If the expansion is legitimately large, raise MAX_EXPANDED_EVIDENCE_ROWS only after confirming the scheduler budget holds.
Example fix
// before
await ingest({ ownerAccountId, companyIds: allSubjects, evidence });
// after
// split subjects so expansion stays under the cap
for (const subjectBatch of chunk(allSubjects, 5)) {
await ingest({ ownerAccountId, companyIds: subjectBatch, evidence });
} Defensive patterns
Strategy: validation
Validate before calling
// estimate worst-case expansion = subjects * rows; keep under 625
const MAX_EXPANSION = 625;
const estimated = companyIds.length * evidence.length;
if (estimated > MAX_EXPANSION) {
// split ingestion
} Type guard
function expansionSafe(subjects: number, rows: number, cap = 625): boolean {
return subjects * rows <= cap;
} Prevention
- Split ingestion so subject-count * row-count stays under MAX_EXPANDED_EVIDENCE_ROWS.
- Tighten provider query/claim matching to reduce routing expansion.
When it happens
Trigger: Provider evidence that, after normalization and per-company routing, expands to more than 625 rows in a single ingestion call.
Common situations: A provider returned rows touching the full cohort (25 companies) with 25+ routed matches each; a query that over-matches claims; the cohort size or claim set grew since the cap was set.
Related errors
- COMPANY_MONITORING_EVIDENCE_SUBJECTS_INVALID
- COMPANY_MONITORING_EVIDENCE_BATCH_INVALID
- INVALID_COMPANY_PATCH
- INVALID_COMPANY_MONITORING_COHORT
- COUNTRIES_LIMIT_EXCEEDED
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/b85649a76a08dedd.
Report an issue: GitHub.