koala73/worldmonitor · error · Error

Compact health payload must be an object

Error message

Compact health payload must be an object

What it means

check-seed-freshness validates the compact health payload fetched from the monitor before inspecting seed freshness. validateCompactHealthPayload requires the payload to be a non-null, non-array object; anything else (null, undefined, a JSON array, a string/number) is rejected. This ensures downstream Object.values/Object.hasOwn checks operate on a real object shape.

Solutions

  1. Log/inspect the fetched payload and fix the fetch/parse step so it yields the compact health object
  2. Ensure the caller awaits response.json() and passes that parsed value, not raw text
  3. Check the health endpoint is returning the compact payload (correct URL/version) rather than an error body
  4. Wrap the call in a try/catch that reports the invalid payload type for diagnosis

Example fix

// before
validateCompactHealthPayload(await res.text());
// after
const payload = await res.json();
if (payload && !Array.isArray(payload)) validateCompactHealthPayload(payload);
Defensive patterns

Strategy: type-guard

Validate before calling

const payload = await res.json();
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('health endpoint did not return an object');

Type guard

const isCompactHealthPayload = (p) => p !== null && typeof p === 'object' && !Array.isArray(p);

Try / catch

try {
  validateCompactHealthPayload(payload);
} catch (e) {
  if (e.message === 'Compact health payload must be an object') {
    console.error('Non-object payload from health endpoint; check fetch/parse and endpoint version');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateCompactHealthPayload with the result of a fetch that returned null/undefined, a JSON array instead of an object, or a string (e.g. an HTML error page parsed loosely or raw text passed through).

Common situations: Health endpoint returned an error page or empty body that got JSON-parsed into something unexpected; API version change reshaped the response into an array; caller passed the raw response text instead of the parsed JSON object.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at scripts/check-seed-freshness.mjs:16

#!/usr/bin/env node

import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { parseArgs } from 'node:util';

const DEFAULT_HEALTH_URL = 'https://api.worldmonitor.app/api/health?compact=1';
const BASELINE_URL = new URL('./seed-freshness-baseline.json', import.meta.url);
// api/health.js only serves a cached verdict for 60 seconds. Allow its maximum
// 20-second request timeout too, so a valid snapshot cannot be rejected solely
// because the response arrived at the end of the monitor's fetch window.
export const MAX_HEALTH_OBSERVATION_AGE_MS = 80 * 1000;

export function validateCompactHealthPayload(payload) {
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
    throw new Error('Compact health payload must be an object');
  }
  if (Object.hasOwn(payload, 'pending')) {
    if (!payload.pending || typeof payload.pending !== 'object' || Array.isArray(payload.pending)) {
      throw new Error('Compact health pending must be an object');
    }
    for (const entry of Object.values(payload.pending)) {
      if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
        throw new Error('Compact health pending entries must be objects');
      }
    }
  }
  // Compact health omits `problems` entirely when every check is healthy.
  if (payload.problems == null && payload.status === 'HEALTHY') return payload;
  if (!payload.problems || typeof payload.problems !== 'object' || Array.isArray(payload.problems)) {
    throw new Error('Compact health payload must contain a problems object');
  }
  return payload;
}

View on GitHub (pinned to 7d06c8633d)