koala73/worldmonitor · error

MND_PROXY_RESPONSE_INVALID

MND_PROXY_RESPONSE_INVALID

Error message

MND_PROXY_RESPONSE_INVALID

What it means

fetchMndViaProxy routes Taiwan MND fetches through a configured proxy function (proxyRequestFn). After the proxy returns, the code validates the envelope: result.status must be an integer HTTP status in 200–599. A missing result, a non-integer status, or a status outside that range means the proxy returned a structurally invalid response, so MND_PROXY_RESPONSE_INVALID is thrown instead of building a Response object.

Solutions

  1. Log the raw proxyRequestFn result to see what shape was actually returned and compare it to the expected {status, buffer} envelope.
  2. Fix the proxy implementation/config so it maps transport failures to a valid HTTP status (200–599) and returns {status, buffer}; a socket failure should surface as its own error, not status 0.
  3. If the proxy library changed its return shape, adapt at the call site (e.g. read result.response.status) or pin/upgrade to a compatible version.
  4. Verify the correct proxyRequestFn is wired for the MND source (not a stub or another source's fetcher).

Example fix

// before: proxy returns transport-level result with no HTTP status
const result = await proxyRequestFn(url, proxyConfig, opts); // { ok: false, err: 'ECONNRESET' }

// after: normalize proxy failures into a valid status envelope before returning
const result = await proxyRequestFn(url, proxyConfig, opts);
if (!result || typeof result.status !== 'number') {
  throw new Error(`PROXY_TRANSPORT_FAILURE:${result?.err ?? 'unknown'}`); // or map to status 502
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the proxy envelope before handing it to fetchMndViaProxy
function isValidProxyStatus(result) {
  return Number.isInteger(result?.status) && result.status >= 200 && result.status <= 599;
}

Type guard

function isMndProxyResult(result) {
  return result != null
    && Number.isInteger(result.status)
    && result.status >= 200 && result.status <= 599
    && (result.status >= 300 || Buffer.isBuffer(result.buffer));
}

Try / catch

try {
  const response = await fetchMndViaProxy(input, init, proxyConfig, proxyRequestFn);
  return response;
} catch (error) {
  if (error?.message === 'MND_PROXY_RESPONSE_INVALID') {
    logger.error({ proxyUrl: proxyConfig.url }, 'proxy returned invalid status envelope');
    throw new SourceUnavailableError('MND proxy misbehaving');
  }
  throw error;
}

Prevention

When it happens

Trigger: proxyRequestFn resolves with null/undefined, an object without a status field, a status like 0, NaN, a string, or a value <200 or >599 (e.g. a custom proxy error code such as 0 or 999) when fetching the Taiwan MND source.

Common situations: Proxy service crashes or returns its own error JSON instead of {status, buffer}; wrong proxyRequestFn injected in tests or DI; proxy library version change altered its return shape (e.g. status moved to result.response.status); network layer returns a 0 status on socket failure.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/c27309e5c763904c. Report an issue: GitHub.

Appendix: source

Thrown at scripts/cross-strait-activity/adapters.mjs:1728

  if (diagnostic) diagnostic.stage = 'parse';
  return { text, status: response.status };
}

async function fetchBoundedText(fetchFn, url, sourceContract, diagnostic = null) {
  const { text } = await fetchBoundedTextWithStatus(fetchFn, url, sourceContract, diagnostic);
  return text;
}

async function fetchMndViaProxy(input, init, proxyConfig, proxyRequestFn) {
  const maxResponseBytes = CROSS_STRAIT_SOURCE_CONTRACTS.taiwanMnd.maxResponseBytes;
  const result = await proxyRequestFn(String(input), proxyConfig, {
    headers: init.headers,
    maxResponseBytes,
    timeoutMs: REQUEST_TIMEOUT_MS,
    signal: init.signal,
  });
  if (!Number.isInteger(result?.status) || result.status < 200 || result.status > 599) {
    throw new Error('MND_PROXY_RESPONSE_INVALID');
  }
  if (result.status >= 300) {
    return new Response(null, { status: result.status });
  }
  if (!Buffer.isBuffer(result.buffer)) throw new Error('MND_PROXY_RESPONSE_INVALID');
  if (result.buffer.byteLength > maxResponseBytes) throw new Error('RESPONSE_TOO_LARGE');
  return new Response(result.status === 204 || result.status === 205 ? null : result.buffer, {
    status: result.status,
  });
}

function shouldProxyJapanModFailure(error) {
  const code = errorCode(error);
  if (code === 'SOURCE_ERROR' || code === 'TIMEOUT' || code === 'JMOD_INDEX_EMPTY') return true;
  const status = Number(/^HTTP_(\d{3})$/u.exec(code)?.[1]);
  return status === 403
    || status === 408
    || status === 425

View on GitHub (pinned to 7d06c8633d)