{"record":{"id":"27f977bb932febf1","repo":"koala73/worldmonitor","slug":"company-monitoring-admission-query-version-invalid","errorCode":"COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID","errorMessage":"COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID","messagePattern":"COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID","errorType":"validation","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/companyMonitoring/admission.ts","lineNumber":105,"sourceCode":"  }\n  const evidence = rows.map((row) => row!);\n  for (const row of evidence) {\n    if (\n      row.occurrenceDedupeKey !== candidate.occurrenceDedupeKey ||\n      (!allowExpired && (\n        row.state !== \"active\" ||\n        (row.expiresAt !== undefined && row.expiresAt <= now)\n      )) ||\n      (row.sourceAuthority === \"verified_first_party\" &&\n        (\n          row.independence !== \"first_party\" ||\n          (row.provider === \"exa\" && row.matchedClaimIds.length === 0)\n        ))\n    ) {\n      throw new ConvexError(\"COMPANY_MONITORING_ADMISSION_EVIDENCE_INVALID\");\n    }\n    if (!row.queryVersion || !ADMISSION_ID.test(row.queryVersion)) {\n      throw new ConvexError(\"COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID\");\n    }\n  }\n  const shaped = evidence.map(evidenceShape);\n  const evidenceSnapshotDigest = await candidateEvidenceSnapshotDigest({\n    selectionPolicyVersion: candidate.selectionPolicyVersion,\n    referenceCount: candidate.referenceCount,\n    referencesTruncated: candidate.referencesTruncated,\n    evidence: shaped,\n  });\n  if (candidate.evidenceSnapshotDigest !== evidenceSnapshotDigest) {\n    throw new ConvexError(\"COMPANY_MONITORING_ADMISSION_EVIDENCE_STALE\");\n  }\n  return shaped;\n}\n\nfunction decisionVersions(candidate: Doc<\"companyMonitoringCandidates\">, modelVersion: string) {\n  return {\n    classificationSchemaVersion: COMPANY_MONITORING_CLASSIFICATION_SCHEMA_VERSION,","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/companyMonitoring/admission.ts#L87-L123","documentation":"Thrown by referencedEvidence() (convex/companyMonitoring/admission.ts:105) when a referenced evidence row has a falsy queryVersion or a queryVersion that fails the ADMISSION_ID regex /^[A-Za-z0-9._:-]{1,128}$/. Every evidence document admitted for classification must carry a well-formed query version identifying the query that produced it.","triggerScenarios":"Evidence rows ingested without setting queryVersion (undefined or empty string); queryVersion populated from a raw URL or free-text label containing /, ?, spaces, or unicode; an ingestion upgrade that renamed the field; rows backfilled by a script that left it null.","commonSituations":"New ingestion pipelines forgetting the field; queryVersion sourced from user input or logs; partial backfills after schema evolution; providers sending query identifiers with characters outside the allowed alphabet.","solutions":["Backfill the evidence documents: set a valid queryVersion (non-empty, <= 128 chars, only A-Za-z0-9._:-) via a one-off mutation","Fix ingestion to require and validate queryVersion with the same regex before insert","After backfill, re-run the scan so candidates reference corrected rows (old candidates still point at the bad digest)","Add a nightly consistency check querying companyMonitoringEvidence for rows where queryVersion is missing or invalid"],"exampleFix":"// ingestion: validate queryVersion exactly like the server (prevents the error)\n// before\nawait ctx.db.insert(\"companyMonitoringEvidence\", { ...row, queryVersion: row.queryVersion ?? \"\" });\n\n// after\nconst ADMISSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nfunction queryVersionOrThrow(value: string | undefined): string {\n  if (!value || !ADMISSION_ID.test(value)) {\n    throw new Error(`queryVersion invalid: ${JSON.stringify(value)}`);\n  }\n  return value;\n}\nawait ctx.db.insert(\"companyMonitoringEvidence\", { ...row, queryVersion: queryVersionOrThrow(row.queryVersion) });","handlingStrategy":"validation","validationCode":"const ADMISSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nfunction hasValidQueryVersion(row: { queryVersion?: string }): boolean {\n  return typeof row.queryVersion === \"string\" && ADMISSION_ID.test(row.queryVersion);\n}\n// ingestion path:\nif (!hasValidQueryVersion(row)) {\n  throw new Error(`evidence row missing valid queryVersion: ${JSON.stringify(row.queryVersion)}`);\n}","typeGuard":"function hasValidQueryVersion(row: { queryVersion?: string }): row is { queryVersion: string } {\n  return typeof row.queryVersion === \"string\" && /^[A-Za-z0-9._:-]{1,128}$/.test(row.queryVersion);\n}","tryCatchPattern":"try {\n  await ctx.runMutation(internal.companyMonitoring.admission.recordClassification, args);\n} catch (err) {\n  if (err instanceof ConvexError && err.data === \"COMPANY_MONITORING_ADMISSION_QUERY_VERSION_INVALID\") {\n    await backfillEvidenceQueryVersion(args.companyId); // then re-scan\n    return;\n  }\n  throw err;\n}","preventionTips":["Require and validate queryVersion in every evidence insert path","Backfill legacy rows before deploying admission code that reads them","Run a periodic query for evidence rows with missing/invalid queryVersion","Keep query identifiers in the allowed alphabet; encode free-form labels"],"tags":["convex","evidence","query-version","validation","backfill"],"backgroundTag":"version-metadata-invalid","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}