koala73/worldmonitor · error · ValidationError

Provide ticker or company

Error message

Provide ticker or company

What it means

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.

Source

Thrown at server/worldmonitor/intelligence/v1/list-company-signals.ts:64

  '4.02': 'Restatement',
  '5.01': 'Control Change',
  '5.02': 'Executive Change',
};

export async function listCompanySignals(
  _ctx: ServerContext,
  req: ListCompanySignalsRequest,
): Promise<ListCompanySignalsResponse> {
  const company = req.company?.trim();
  const domain = req.domain?.trim().toLowerCase();
  const ticker = req.ticker?.trim();

  // Preserve the disabled v1 domain contract. Domain-derived attribution is
  // still forbidden; callers using that deprecated field get an empty stub.
  if (domain) return emptyResponse(company || '', false, domain);

  if (!company && !ticker) {
    throw new ValidationError([{ field: 'company', description: 'Provide ticker or company' }]);
  }

  const resolution = await resolveCompany({ ticker, name: company });
  if (resolution.status !== 'ok') {
    // "not_found" is a real, cacheable answer; "registry_unavailable" is a
    // lookup failure and must not be cached as one.
    return emptyResponse(
      company || ticker?.toUpperCase() || '',
      resolution.status === 'registry_unavailable',
    );
  }
  const resolved = resolution.company;

  const [submissions, news] = await Promise.all([
    fetchSecSubmissions(resolved.cik),
    fetchCompanyNewsMentions(resolved.ticker, resolved.name),
  ]);

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Validate that company or ticker is present (and non-blank) before invoking the RPC
  2. When resolving from user text, put it in company (name resolution) or ticker, and skip blank rows
  3. Check the field names against the proto — 'name' sent instead of 'company' leaves both empty and triggers this
Defensive patterns

Strategy: validation

Validate before calling

const company = req.company?.trim();
const ticker = req.ticker?.trim();
if (!company && !ticker) return skipRow(row); // e.g. watchlist row without identifiers

Type guard

function isMissingCompanyViolation(body: unknown): boolean {
  const v = (body as { violations?: { field?: string; description?: string }[] })?.violations;
  return Array.isArray(v) && v.some((x) => x.field === 'company' && x.description === 'Provide ticker or company');
}

Try / catch

try {
  await listCompanySignals({ company, ticker });
} catch (e) {
  if (e instanceof HttpError && e.status === 400 && isMissingCompanyViolation(e.body)) {
    return promptForCompany();
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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').

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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