koala73/worldmonitor · error

Generic adapter requires acquisition config (retailer: ${ctx

Error message

Generic adapter requires acquisition config (retailer: ${ctx.config.slug})

What it means

GenericPlaywrightAdapter.fetchTarget() delegates URL acquisition to fetchWithFallback(), which needs the retailer config's acquisition block (provider, options, optional fallback). The error fires deterministically when a retailer using the generic adapter has no acquisition field — the adapter has no other way to know which provider chain to use for its discovery seeds.

Source

Thrown at consumer-prices-core/src/adapters/generic.ts:58

    return el?.getAttribute(attr.trim()) ?? null;
  }

  return doc.querySelector(selector)?.textContent?.trim() ?? null;
}

export class GenericPlaywrightAdapter implements RetailerAdapter {
  readonly key = 'generic';

  async discoverTargets(ctx: AdapterContext): Promise<Target[]> {
    return ctx.config.discovery.seeds.map((s) => ({
      id: s.id,
      url: s.url.startsWith('http') ? s.url : `${ctx.config.baseUrl}${s.url}`,
      category: s.category ?? s.id,
    }));
  }

  async fetchTarget(ctx: AdapterContext, target: Target): Promise<FetchResult> {
    if (!ctx.config.acquisition) throw new Error(`Generic adapter requires acquisition config (retailer: ${ctx.config.slug})`);
    const result = await fetchWithFallback(target.url, ctx.config.acquisition, ctx.config.rateLimit ? {
      timeout: 30_000,
    } : undefined);

    return {
      url: result.url,
      html: result.html,
      markdown: result.markdown,
      statusCode: result.statusCode,
      fetchedAt: result.fetchedAt,
    };
  }

  async parseListing(ctx: AdapterContext, result: FetchResult): Promise<ParsedProduct[]> {
    const selectors = ctx.config.extraction?.productCard;
    if (!selectors) return [];

    const dom = new JSDOM(result.html);

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Add an acquisition block to the retailer config: { provider: 'playwright'|'firecrawl'|'p0', options?, fallback? }
  2. Validate retailer configs at load time (adapter-specific required fields) so this fails at startup, not per-target
  3. Check for schema drift — the field must be exactly 'acquisition' on the retailer config object

Example fix

// before
{ slug: 'retailer-x', baseUrl: 'https://x.example', adapter: 'generic', discovery: { seeds: [...] } }

// after
{
  slug: 'retailer-x', baseUrl: 'https://x.example', adapter: 'generic',
  discovery: { seeds: [...] },
  acquisition: { provider: 'playwright', fallback: 'firecrawl' },
}
Defensive patterns

Strategy: validation

Validate before calling

function validateGenericConfig(cfg: RetailerConfig): string[] {
  const errs: string[] = [];
  if (cfg.adapter === 'generic' && !cfg.acquisition) {
    errs.push(`retailer '${cfg.slug}': generic adapter requires an acquisition block`);
  }
  return errs;
}
// at startup
const errs = retailerConfigs.flatMap(validateGenericConfig);
if (errs.length) throw new Error(errs.join('; '));

Type guard

function hasAcquisition(cfg: RetailerConfig): cfg is RetailerConfig & { acquisition: AcquisitionConfig } {
  return cfg.acquisition !== undefined;
}

Try / catch

try {
  await adapter.fetchTarget(ctx, target);
} catch (err) {
  if (err instanceof Error && err.message.includes('requires acquisition config')) {
    skipRetailer(ctx.config.slug, 'misconfigured'); // permanent config bug: skip and alert, do not retry
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: A retailer config with adapter 'generic' and discovery.seeds but a missing or renamed acquisition section; acquisition nested at the wrong level after a config schema change; configs written for a different adapter being switched to generic without adding the field.

Common situations: Adding a new retailer from an old template that predates the acquisition field; a config schema migration renaming the block; hand-edited configs in a deploy repo drifting from the schema.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


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