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
- 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
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
- 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
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
- Physical divergence snapshot must contain gold and silver re
- ${name} startCommand must be a non-empty string
- ${name} requiredEnv must be an array
- Compact health payload must be an object
- Compact health pending must be an object
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)