koala73/worldmonitor · error

EXA_API_KEY is required for exa-search adapter

Error message

EXA_API_KEY is required for exa-search adapter

What it means

ExaSearchAdapter.fetchTarget() guards on this.apiKey and throws before any network call. The adapter is constructible with an empty key — the constructor does not validate — so the misconfiguration survives until the first target is fetched, even though the class ships validateConfig(), which reports 'EXA_API_KEY env var is required for adapter: exa-search' for exactly this case.

Source

Thrown at consumer-prices-core/src/adapters/exa-search.ts:136

    }

    return targets;
  }

  private buildQuery(canonicalName: string, currency: string, marketCode: string, template?: string): string {
    const market = MARKET_NAMES[marketCode] ?? '';
    if (template) {
      return template
        .replace('{canonicalName}', canonicalName)
        .replace('{currency}', currency)
        .replace('{market}', market)
        .trim();
    }
    return `${canonicalName} ${market} ${currency} price`.trim();
  }

  async fetchTarget(ctx: AdapterContext, target: Target): Promise<FetchResult> {
    if (!this.apiKey) throw new Error('EXA_API_KEY is required for exa-search adapter');

    const { canonicalName, domain, currency, basketSlug } = target.metadata as {
      canonicalName: string;
      domain: string;
      currency: string;
      basketSlug: string;
    };

    const searchQuery = this.buildQuery(
      canonicalName,
      currency,
      ctx.config.marketCode,
      ctx.config.acquisition?.searchQueryTemplate,
    );

    const body = {
      query: searchQuery,
      numResults: 5,

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Set EXA_API_KEY in the environment the adapter process reads
  2. Call adapter.validateConfig(retailerConfig) during bootstrap and refuse to start when it returns errors
  3. Fail adapter construction on a missing key so the failure happens at wiring time, not per-target
  4. Log which retailer configs select the exa-search adapter so the blast radius is obvious

Example fix

// before — constructible with an empty key, fails per-target later
const adapter = new ExaSearchAdapter(process.env.EXA_API_KEY ?? '');

// after — fail at construction; keep validateConfig as the config-level check
const key = process.env.EXA_API_KEY;
if (!key) throw new Error('EXA_API_KEY is not set — cannot wire exa-search adapter');
const adapter = new ExaSearchAdapter(key);
Defensive patterns

Strategy: validation

Validate before calling

const errs = await adapter.validateConfig(retailerConfig);
if (errs.length) throw new Error(`exa-search config invalid: ${errs.join('; ')}`);

Type guard

function exaAdapterReady(adapter: ExaSearchAdapter): boolean {
  // mirror the internal guard without triggering the per-target throw
  return Boolean((adapter as unknown as { apiKey?: string }).apiKey);
}

Try / catch

try {
  await adapter.fetchTarget(ctx, target);
} catch (err) {
  if (err instanceof Error && err.message.includes('EXA_API_KEY is required')) {
    abortRun('exa-search misconfigured'); // config error: stop, do not retry — it cannot succeed
  }
  throw err;
}

Prevention

When it happens

Trigger: Constructing ExaSearchAdapter('') or with process.env.EXA_API_KEY undefined, then running the adapter pipeline; executing the exa-search adapter in CI without secrets mounted; skipping the validateConfig() step during adapter bootstrap.

Common situations: A new environment (local/CI/staging) missing EXA_API_KEY while production has it; secrets mounted after process start; the exa-search adapter registered as the default for search retailers with no startup config check.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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