--- date: 2026-10-18 branch: fix/renovate-invalid-config title: "renovate.json was invalid, so the first manual run errored out" --- ## What changed The operator ran the Renovate workflow manually and it **failed**. The cause is not the workflow: **`renovate.json` was never a valid Renovate config.** It carried five pseudo-comment keys — `_comment`, `_kubernetes_comment`, `_unmanageable_comment`, `_ignorePaths_comment`, `_docker_comment` — because JSON has no comments. Renovate **rejects unknown top-level options**, so the entire config failed validation. Proven with the official validator rather than inferred: ``` $ podman run --rm -v "$PWD/renovate.json:/tmp/renovate.json:ro,Z" \ ghcr.io/renovatebot/renovate renovate-config-validator /tmp/renovate.json ERROR: Found errors in configuration Invalid configuration option: _comment Invalid configuration option: _docker_comment Invalid configuration option: _ignorePaths_comment Invalid configuration option: _kubernetes_comment Invalid configuration option: _unmanageable_comment ``` This is the second independent reason Renovate had never worked, on top of PR #77's cron firing outside `renovate.json`'s own schedule window. Either alone was sufficient to produce "nothing ever happens"; #73 shipped both. **Converted `renovate.json` → `renovate.json5`.** JSON5 permits real `//` comments, and Renovate discovers `renovate.json5` natively — nothing in `.gitea/` or `ci/` pins the old filename (only prose inside comments), and no `RENOVATE_CONFIG_FILE` is set. Every word of the original rationale is preserved as comments positioned next to what it explains, which a flattened `description` string would have lost. A header on the new file records the failure, the validator command, and the instruction to run it before committing — this class of bug is invisible to the repo's own gate. ## What I verified * **The exact failure**, from the official validator, quoted above. Not read off a log, not inferred from the workflow. * **The fix validates**: the same validator against `renovate.json5` returns `INFO: Config validated successfully against 1 file(s)`. * **Nothing was dropped in the conversion**, checked programmatically against `git show HEAD:renovate.json`: 18 non-comment keys before, 18 after, **no missing keys, no extra keys, and no key whose value changed**. `docker: {enabled: true}` confirmed still present — the validator prints a normalisation diff that mentions it, which is its own migration suggestion, not a removal. * **74 comment lines survive** in the new file, so the rationale is intact. * **`RENOVATE_TOKEN` exists** as a repository Actions secret (listed by name via the Gitea API), so the workflow's fail-hard preflight is satisfied. * **A suspicion was DISPROVED:** `enabledManagers` containing bare `regex` was thought to need to be `custom.regex`, which would have silently disabled every inline `# renovate:` marker. The validator accepts `regex`, so that concern was unfounded and nothing here changes it. * Nothing functionally references `renovate.json` by name — `grep -rn "renovate\.json" .gitea/ ci/ conformance/` returns only comment prose. ## What I did NOT verify * **Renovate has still never completed a run.** This removes one of two blockers; PR #77 removes the other. Neither is merged, so **no successful Renovate run has been observed** and I cannot say the bot works — only that these two specific defects are fixed. * **I did not re-run the workflow after the fix.** The honest test is a `workflow_dispatch` with `dry_run: true` once both this and #77 are merged. Expect ~30 dependencies behind, rate-limited to `prHourlyLimit: 2` / `prConcurrentLimit: 5`. * **Whether the managers actually extract anything.** A valid config that matches no files is still a no-op. The dry-run log's per-manager dependency counts are the only thing that proves the `managerFilePatterns` are right, and I have not seen them. * **Whether `renovate.json5` is honoured by the pinned Renovate version in the workflow.** JSON5 discovery is long-standing and the validator accepted the file, but the workflow's image tag was not cross-checked against the version that added it. * **Nothing in the gate catches an invalid Renovate config.** This bug would have recurred silently. A one-line L0 check shelling out to `renovate-config-validator` would close it; it is not written here, and `conformance/**` is a sensitive path. * I did not re-measure branch protection or runner state (CLAUDE.md §2b).