koala73/worldmonitor · warning · ValidationError

alertThreshold must be between 0 and 100

Error message

alertThreshold must be between 0 and 100

What it means

registerWebhook re-enforces the inclusive 0..100 range on alertThreshold even though buf.validate normally enforces it at the wire layer, because direct handler calls (internal jobs, test harnesses, transports that bypass proto validation) would otherwise store out-of-range values. undefined means the partner omitted the field and defaults to 50; an explicit 0 is preserved and means deliver every alert.

Source

Thrown at server/worldmonitor/shipping/v2/register-webhook.ts:78

  }

  const chokepointIds = Array.isArray(req.chokepointIds) ? req.chokepointIds : [];
  const invalidCp = chokepointIds.find(id => !VALID_CHOKEPOINT_IDS.has(id));
  if (invalidCp) {
    throw new ValidationError([
      { field: 'chokepointIds', description: `Unknown chokepoint ID: ${invalidCp}` },
    ]);
  }

  // alert_threshold is `optional int32` (#3242 followup #4) — undefined means
  // the partner omitted the field, so apply the legacy default of 50. An
  // explicit 0 is preserved (deliver every alert). The 0..100 range is
  // normally enforced by buf.validate at the wire layer, but we re-enforce
  // it here so direct handler calls (internal jobs, test harnesses, future
  // transports that bypass buf.validate) can't store out-of-range values.
  const alertThreshold = req.alertThreshold ?? 50;
  if (alertThreshold < 0 || alertThreshold > 100) {
    throw new ValidationError([
      { field: 'alertThreshold', description: 'alertThreshold must be between 0 and 100' },
    ]);
  }

  const ownerTag = await callerFingerprint(ctx.request, apiKeyResult.credential);
  const newSubscriberId = generateSubscriberId();
  const secret = await generateSecret();

  const record: WebhookRecord = {
    subscriberId: newSubscriberId,
    ownerTag,
    callbackUrl,
    chokepointIds: chokepointIds.length ? chokepointIds : [...VALID_CHOKEPOINT_IDS],
    alertThreshold,
    createdAt: new Date().toISOString(),
    active: true,
    secret,
  };

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Clamp or validate alertThreshold to an integer in 0..100 before building the request
  2. Keep explicit 0 if 'deliver every alert' was intended — only <0 or >100 throws
  3. For direct handler calls, route requests through the same validation path as the wire layer or validate first

Example fix

// before
registerWebhook(ctx, { callbackUrl, chokepointIds, alertThreshold: 150 });
// after
registerWebhook(ctx, {
  callbackUrl,
  chokepointIds,
  alertThreshold: Math.min(100, Math.max(0, Math.round(150))),
});
Defensive patterns

Strategy: validation

Validate before calling

if (req.alertThreshold !== undefined && !(Number.isInteger(req.alertThreshold) && req.alertThreshold >= 0 && req.alertThreshold <= 100)) throw new RangeError('alertThreshold must be an integer in 0..100');

Type guard

const isAlertThreshold = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= 100;

Try / catch

catch (e) { if (e?.details?.[0]?.field === 'alertThreshold') { clamp to [0,100] and re-submit, or omit the field to take the default 50 } else throw e; }

Prevention

When it happens

Trigger: Direct (non-HTTP) invocation of the registerWebhook handler with alertThreshold below 0 or above 100; a transport wired without buf.validate; hand-built request objects in tests or internal jobs. Over-the-wire calls are normally stopped earlier by buf.validate with a different error shape.

Common situations: Unit tests constructing requests by hand; a UI slider allowing out-of-range values; internal automation jobs calling the handler directly; a new RPC transport added without the validation middleware.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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