{"record":{"id":"4d7f61ab6304d130","repo":"koala73/worldmonitor","slug":"provide-ticker-or-company","errorCode":null,"errorMessage":"Provide ticker or company","messagePattern":"Provide ticker or company","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/intelligence/v1/list-company-signals.ts","lineNumber":64,"sourceCode":"  '4.02': 'Restatement',\n  '5.01': 'Control Change',\n  '5.02': 'Executive Change',\n};\n\nexport async function listCompanySignals(\n  _ctx: ServerContext,\n  req: ListCompanySignalsRequest,\n): Promise<ListCompanySignalsResponse> {\n  const company = req.company?.trim();\n  const domain = req.domain?.trim().toLowerCase();\n  const ticker = req.ticker?.trim();\n\n  // Preserve the disabled v1 domain contract. Domain-derived attribution is\n  // still forbidden; callers using that deprecated field get an empty stub.\n  if (domain) return emptyResponse(company || '', false, domain);\n\n  if (!company && !ticker) {\n    throw new ValidationError([{ field: 'company', description: 'Provide ticker or company' }]);\n  }\n\n  const resolution = await resolveCompany({ ticker, name: company });\n  if (resolution.status !== 'ok') {\n    // \"not_found\" is a real, cacheable answer; \"registry_unavailable\" is a\n    // lookup failure and must not be cached as one.\n    return emptyResponse(\n      company || ticker?.toUpperCase() || '',\n      resolution.status === 'registry_unavailable',\n    );\n  }\n  const resolved = resolution.company;\n\n  const [submissions, news] = await Promise.all([\n    fetchSecSubmissions(resolved.cik),\n    fetchCompanyNewsMentions(resolved.ticker, resolved.name),\n  ]);\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/server/worldmonitor/intelligence/v1/list-company-signals.ts#L46-L82","documentation":"listCompanySignals throws ValidationError (field 'company') when both req.company and req.ticker are empty after trimming. As with its sibling enrichment endpoint, the v1 domain contract is preserved: a request carrying the deprecated domain field returns an empty stub response and never throws this error.","triggerScenarios":"Calling /api/intelligence/v1/list-company-signals with neither company nor ticker; whitespace-only values for both; a watchlist sync that includes rows with no identifiers.","commonSituations":"Empty search box submitted from a UI; batch job iterating company records where some rows lack both fields; parameter name confusion (sending 'name' or 'q' instead of 'company'/'ticker').","solutions":["Validate that company or ticker is present (and non-blank) before invoking the RPC","When resolving from user text, put it in company (name resolution) or ticker, and skip blank rows","Check the field names against the proto — 'name' sent instead of 'company' leaves both empty and triggers this"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const company = req.company?.trim();\nconst ticker = req.ticker?.trim();\nif (!company && !ticker) return skipRow(row); // e.g. watchlist row without identifiers","typeGuard":"function isMissingCompanyViolation(body: unknown): boolean {\n  const v = (body as { violations?: { field?: string; description?: string }[] })?.violations;\n  return Array.isArray(v) && v.some((x) => x.field === 'company' && x.description === 'Provide ticker or company');\n}","tryCatchPattern":"try {\n  await listCompanySignals({ company, ticker });\n} catch (e) {\n  if (e instanceof HttpError && e.status === 400 && isMissingCompanyViolation(e.body)) {\n    return promptForCompany();\n  }\n  throw e;\n}","preventionTips":["Filter blank rows out of batch/watchlist syncs before calling the RPC","Use the exact proto field names ('company', not 'name'/'q') so values are not silently dropped","Remember domain-only requests return an empty stub by v1 contract — they never hit this error"],"tags":["intelligence","validation","missing-parameter","signals"],"backgroundTag":"missing-required-argument","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}