{"record":{"id":"e46d50fa6f91370e","repo":"koala73/worldmonitor","slug":"name-cronschedule-must-be-a-string-or-null","errorCode":null,"errorMessage":"${name} cronSchedule must be a string or null","messagePattern":"(.+?) cronSchedule must be a string or null","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/audit-railway-watch-paths.mjs","lineNumber":191,"sourceCode":"    );\n  }\n  if (hasOwn(entry, 'dockerfile')\n    && (typeof entry.dockerfile !== 'string' || normalizeDockerfilePath(entry.dockerfile).length === 0)) {\n    throw new Error(`${name} dockerfile must be a non-empty string`);\n  }\n  if (entry.deployMode === 'dockerfile' && !hasOwn(entry, 'dockerfile')) {\n    throw new Error(`${name} deployMode dockerfile requires a dockerfile path`);\n  }\n  if (hasOwn(entry, 'watchPatterns')) {\n    if (!Array.isArray(entry.watchPatterns)\n      || entry.watchPatterns.some((pattern) => typeof pattern !== 'string')) {\n      throw new Error(`${name} watchPatterns must be an array of strings`);\n    }\n  }\n  if (hasOwn(entry, 'cronSchedule')\n    && entry.cronSchedule !== null\n    && typeof entry.cronSchedule !== 'string') {\n    throw new Error(`${name} cronSchedule must be a string or null`);\n  }\n  if (hasOwn(entry, 'startCommand')\n    && (typeof entry.startCommand !== 'string' || entry.startCommand.trim().length === 0)) {\n    throw new Error(`${name} startCommand must be a non-empty string`);\n  }\n  if (hasOwn(entry, 'requiredEnv')) {\n    if (!Array.isArray(entry.requiredEnv)) {\n      throw new Error(`${name} requiredEnv must be an array`);\n    }\n    for (const requirement of entry.requiredEnv) {\n      const alternatives = Array.isArray(requirement) ? requirement : [requirement];\n      if (alternatives.length === 0) {\n        throw new Error(`${name} requiredEnv contains an empty any-of group`);\n      }\n      for (const variable of alternatives) {\n        if (typeof variable !== 'string' || !/^[A-Z][A-Z0-9_]*$/.test(variable)) {\n          throw new Error(`${name} has invalid requiredEnv name ${JSON.stringify(variable)}`);\n        }","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/audit-railway-watch-paths.mjs#L173-L209","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the offending entry so `cronSchedule` is a valid cron string (e.g. '*/30 * * * *') or remove the key / set it to null","Run the audit script again to confirm the registry passes all assertRegistryEntry checks","If entries are generated programmatically, coerce or validate the schedule field to string|null at generation time","Add a JSON-schema or editor validation for the registry file to catch type drift early"],"exampleFix":"// before\n{ name: 'weather-worker', cronSchedule: 30 }\n// after\n{ name: 'weather-worker', cronSchedule: '*/30 * * * *' }","handlingStrategy":"validation","validationCode":"function cronScheduleIsValid(entry) {\n  return !Object.prototype.hasOwnProperty.call(entry, 'cronSchedule')\n    || entry.cronSchedule === null\n    || typeof entry.cronSchedule === 'string';\n}\nif (!cronScheduleIsValid(registryEntry)) throw new Error('cronSchedule must be a string or null');","typeGuard":"const hasValidCronSchedule = (e) => e.cronSchedule === undefined || e.cronSchedule === null || typeof e.cronSchedule === 'string';","tryCatchPattern":"try {\n  assertRegistryEntry(name, entry);\n} catch (err) {\n  if (String(err.message).endsWith('cronSchedule must be a string or null')) {\n    console.error(`Fix registry entry '${name}': cronSchedule must be a cron string or null`);\n  }\n}","preventionTips":["Validate the registry file with a JSON schema in CI before the audit runs","Only express 'no schedule' as omitted key or explicit null — never 0/empty object","Coerce generated entries: String(schedule) or null at write time","Add editor schema/lint support for the registry file to catch type drift on save"],"tags":["validation","config","schema","railway"],"backgroundTag":"config-type-mismatch","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}