tinyhumansai/openhuman · error

Missing schedule and legacy expression for cron job

Error message

Missing schedule and legacy expression for cron job

What it means

decode_schedule reconstructs a job's Schedule from the DB row: the structured schedule JSON column when present and non-empty, otherwise the legacy expression column. When both are empty the row cannot yield any schedule and decoding bails — this is a corrupted or partially-written cron_jobs row, not a user input error.

Source

Thrown at src/openhuman/cron/store.rs:718

            Some(raw) => Some(parse_rfc3339(&raw).map_err(sql_conversion_error)?),
            None => None,
        },
        last_status: row.get(15)?,
        last_output: row.get(16)?,
    })
}

fn decode_schedule(schedule_raw: Option<&str>, expression: &str) -> Result<Schedule> {
    if let Some(raw) = schedule_raw {
        let trimmed = raw.trim();
        if !trimmed.is_empty() {
            return serde_json::from_str(trimmed)
                .with_context(|| format!("Failed to parse cron schedule JSON: {trimmed}"));
        }
    }

    if expression.trim().is_empty() {
        anyhow::bail!("Missing schedule and legacy expression for cron job")
    }

    Ok(Schedule::Cron {
        expr: expression.to_string(),
        tz: None,
        active_hours: None,
    })
}

fn decode_delivery(delivery_raw: Option<&str>) -> Result<DeliveryConfig> {
    if let Some(raw) = delivery_raw {
        let trimmed = raw.trim();
        if !trimmed.is_empty() {
            return serde_json::from_str(trimmed)
                .with_context(|| format!("Failed to parse cron delivery JSON: {trimmed}"));
        }
    }
    Ok(DeliveryConfig::default())

View on GitHub (pinned to 7491200858)

Solutions

  1. Repair the row: set a valid schedule JSON (or a legacy expression) via sqlite3 against the workspace's cron.db
  2. If the job is dispensable, DELETE the broken row and re-create the job through CLI/RPC
  3. If many rows are affected, restore the DB from backup rather than patching row by row

Example fix

-- before: row has empty schedule AND empty expression
-- after: repair with a valid schedule JSON
UPDATE cron_jobs
SET schedule = '{"type":"cron","expr":"0 9 * * *","tz":null,"active_hours":null}'
WHERE id = 'broken-job-id';
Defensive patterns

Strategy: try-catch

Validate before calling

-- integrity check runnable before operations
SELECT id FROM cron_jobs
WHERE COALESCE(TRIM(schedule), '') = ''
  AND COALESCE(TRIM(expression), '') = '';
-- any rows returned will fail decode_schedule

Try / catch

When listing jobs, wrap per-row mapping so one corrupt row degrades to a skipped entry with a warning instead of failing the entire cron list operation.

Prevention

When it happens

Trigger: Rows touched by hand-edited cron.db (an UPDATE that cleared expression), a failed or partial migration, an interrupted restore, or a writer that inserted a job without either field. Any later cron list/get over the table hits the bail during row mapping.

Common situations: Manual sqlite3 surgery on the workspace DB; backup/sync tools copying the DB mid-write; importing jobs from an old version whose writer format changed.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/5047c235eea9cbf1. Report an issue: GitHub.