{"record":{"id":"19578188ee655aae","repo":"affaan-m/ECC","slug":"legacy-scheduler-jobs-file-must-be-a-json-object-o","errorCode":null,"errorMessage":"legacy scheduler jobs file must be a JSON object or array: {}","messagePattern":"legacy scheduler jobs file must be a JSON object or array: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/main.rs","lineNumber":5530,"sourceCode":"    let source_path = jobs_path\n        .strip_prefix(source)\n        .unwrap_or(&jobs_path)\n        .display()\n        .to_string();\n\n    let entries: Vec<&serde_json::Value> = match &value {\n        serde_json::Value::Array(items) => items.iter().collect(),\n        serde_json::Value::Object(map) => {\n            if let Some(items) = [\"jobs\", \"schedules\", \"tasks\"]\n                .iter()\n                .find_map(|key| map.get(*key).and_then(serde_json::Value::as_array))\n            {\n                items.iter().collect()\n            } else {\n                vec![&value]\n            }\n        }\n        _ => anyhow::bail!(\n            \"legacy scheduler jobs file must be a JSON object or array: {}\",\n            jobs_path.display()\n        ),\n    };\n\n    Ok(entries\n        .into_iter()\n        .enumerate()\n        .map(|(index, value)| build_legacy_schedule_draft(value, index, &source_path))\n        .collect())\n}\n\nfn load_legacy_remote_dispatch_drafts(source: &Path) -> Result<Vec<LegacyRemoteDispatchDraft>> {\n    let gateway_dir = source.join(\"gateway\");\n    if !gateway_dir.is_dir() {\n        return Ok(Vec::new());\n    }\n","sourceCodeStart":5512,"sourceCodeEnd":5548,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/main.rs#L5512-L5548","documentation":"The legacy scheduler import parses the jobs file as JSON and accepts only a top-level object or array; any other JSON top-level value (string, number, boolean, null) triggers this bail with the file path. The importer needs a collection shape to extract job entries.","triggerScenarios":"Pointing the legacy schedule import at a JSON file whose root is a scalar (e.g. a file containing `{}` is fine, but `\"jobs\"` or `42` is not); feeding the wrong file (a settings file or scalar export) as the jobs file.","commonSituations":"An export tool wrote a scalar/null (e.g. when there were no jobs it wrote `null`); the user passed the wrong JSON file to the importer; an older export format with a different root shape.","solutions":["Check the file's top-level value (`jq 'type' <file>`) and fix the export to produce an object or array of jobs.","Point the import at the correct legacy jobs JSON file.","If the export is `null` because there were no jobs, skip the import or replace the file with `[]`."],"exampleFix":"// before (jobs.json)\nnull\n\n// after (jobs.json)\n[]","handlingStrategy":"validation","validationCode":"// Check the JSON root shape before import\nconst jobs = JSON.parse(fs.readFileSync(jobsPath, 'utf8'));\nif (jobs === null || typeof jobs !== 'object') {\n  throw new Error(`jobs file root must be object or array: ${jobsPath}`);\n}","typeGuard":"function isJobCollection(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v)\n    ? true\n    : Array.isArray(v);\n}","tryCatchPattern":"match parse_legacy_jobs(&jobs_path) {\n    Err(e) if e.to_string().contains(\"must be a JSON object or array\") => {\n        eprintln!(\"Fix the export or point at the correct jobs file: {}\", jobs_path.display());\n    }\n    other => other?,\n}","preventionTips":["Run `jq 'type' file.json` to verify the root shape before importing.","Ensure your export tool writes `[]` (not `null`) when there are no jobs.","Add a JSON schema for the jobs file and validate exports at creation time."],"tags":["json","migration","rust"],"backgroundTag":"schema-validation-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}