koala73/worldmonitor · error · Error

${name} cronSchedule must be a string or null

Error message

${name} cronSchedule must be a string or null

What it means

Validation error from assertRegistryEntry in the Railway watch-path audit script: each registry entry's optional `cronSchedule` field, when present, must be either a JSON string or explicit null. Any other type (number, boolean, object, array) is rejected so the Railway job registry stays schema-consistent before auditing deploy watch paths.

Solutions

  1. Change the offending entry so `cronSchedule` is a valid cron string (e.g. '*/30 * * * *') or remove the key / set it to null
  2. Run the audit script again to confirm the registry passes all assertRegistryEntry checks
  3. If entries are generated programmatically, coerce or validate the schedule field to string|null at generation time
  4. Add a JSON-schema or editor validation for the registry file to catch type drift early

Example fix

// before
{ name: 'weather-worker', cronSchedule: 30 }
// after
{ name: 'weather-worker', cronSchedule: '*/30 * * * *' }
Defensive patterns

Strategy: validation

Validate before calling

function cronScheduleIsValid(entry) {
  return !Object.prototype.hasOwnProperty.call(entry, 'cronSchedule')
    || entry.cronSchedule === null
    || typeof entry.cronSchedule === 'string';
}
if (!cronScheduleIsValid(registryEntry)) throw new Error('cronSchedule must be a string or null');

Type guard

const hasValidCronSchedule = (e) => e.cronSchedule === undefined || e.cronSchedule === null || typeof e.cronSchedule === 'string';

Try / catch

try {
  assertRegistryEntry(name, entry);
} catch (err) {
  if (String(err.message).endsWith('cronSchedule must be a string or null')) {
    console.error(`Fix registry entry '${name}': cronSchedule must be a cron string or null`);
  }
}

Prevention

When it happens

Trigger: A registry entry defines `cronSchedule` with a non-string, non-null value — e.g. `cronSchedule: 30`, `cronSchedule: { every: 'hour' }`, or `cronSchedule: true` — while assertRegistryEntry validates the entry via hasOwn checks.

Common situations: Hand-editing the registry JSON/JS and using a numeric shorthand for a schedule; copying a cron config from another tool that uses objects; a migration or script generating entries with wrong types; forgetting that omitting the key and null are the only 'no schedule' representations.

Related errors


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

Appendix: source

Thrown at scripts/audit-railway-watch-paths.mjs:191

    );
  }
  if (hasOwn(entry, 'dockerfile')
    && (typeof entry.dockerfile !== 'string' || normalizeDockerfilePath(entry.dockerfile).length === 0)) {
    throw new Error(`${name} dockerfile must be a non-empty string`);
  }
  if (entry.deployMode === 'dockerfile' && !hasOwn(entry, 'dockerfile')) {
    throw new Error(`${name} deployMode dockerfile requires a dockerfile path`);
  }
  if (hasOwn(entry, 'watchPatterns')) {
    if (!Array.isArray(entry.watchPatterns)
      || entry.watchPatterns.some((pattern) => typeof pattern !== 'string')) {
      throw new Error(`${name} watchPatterns must be an array of strings`);
    }
  }
  if (hasOwn(entry, 'cronSchedule')
    && entry.cronSchedule !== null
    && typeof entry.cronSchedule !== 'string') {
    throw new Error(`${name} cronSchedule must be a string or null`);
  }
  if (hasOwn(entry, 'startCommand')
    && (typeof entry.startCommand !== 'string' || entry.startCommand.trim().length === 0)) {
    throw new Error(`${name} startCommand must be a non-empty string`);
  }
  if (hasOwn(entry, 'requiredEnv')) {
    if (!Array.isArray(entry.requiredEnv)) {
      throw new Error(`${name} requiredEnv must be an array`);
    }
    for (const requirement of entry.requiredEnv) {
      const alternatives = Array.isArray(requirement) ? requirement : [requirement];
      if (alternatives.length === 0) {
        throw new Error(`${name} requiredEnv contains an empty any-of group`);
      }
      for (const variable of alternatives) {
        if (typeof variable !== 'string' || !/^[A-Z][A-Z0-9_]*$/.test(variable)) {
          throw new Error(`${name} has invalid requiredEnv name ${JSON.stringify(variable)}`);
        }

View on GitHub (pinned to 7d06c8633d)