On 53 failed builds and a stale workflow
I was in the middle of a session when an unexpected notification pinged my inbox.
It was an automated alert from GitHub, dryly informing me that it had disabled a scheduled cron workflow on my OpenClaw repository because the project hadn’t seen any direct commit activity in over 60 days.
I hadn’t even realised that was a thing.
Curious about what else had been quietly pausing in the background, I logged into my console and opened the Actions tab for my personal blog. Instead of the clean, green dashboard I expected, I was met with a brutal, scrolling wall of red — 53 failed builds, stretching back over weeks.
The site looked perfectly fine in my browser, yet behind the scenes, my deployment pipelines had been silently crashing in the dark.
I sat down with Zuri to dissect exactly what was triggering this silent bloodbath, how GitHub’s “stale workflow” lockouts actually work, and how we could build a bulletproof guardrail to ensure it never happens again. Here is what we uncovered.
TL;DR: the rapid-hardening check
If you want to keep your Astro builds green and prevent GitHub Actions from silently dropping your scheduled workflows, implement these three practices immediately:
- Verify your builds locally before pushing:
pnpm build - Automate safety with a Git pre-push hook (
.git/hooks/pre-push):#!/bin/sh echo "=== Verifying build before push ===" pnpm build || { echo "❌ Build failed! Push aborted."; exit 1; } - Wake up idle repositories: Pushing a commit or manually triggering a workflow once every 60 days will keep your scheduled crons (automated scrapers, bots, backup routines) active.
That unexpected email from GitHub
The first shock of the day was learning about GitHub’s “inactive repository” policy.
If you have scheduled workflows — like a daily data scraper, an automated content curator, or a server health checker — running on a repository, GitHub Actions will quietly pause them if the repo sees no direct code commits for 60 consecutive days.
Think of it like hiring a nightwatchman to guard your gate. If no visitors drive in or out of the estate for two months, the guard gets bored, puts on their headphones, falls asleep, and stops looking at the gate entirely. The only way to get them back on duty is to walk up and physically tap them on the shoulder (manually click “Enable workflow” on the Actions page or push a dummy commit).
If you are running self-improving agents, autonomous pipelines, or long-term monitoring bots that are designed to run indefinitely without manual intervention, this silent pause is a major hazard. It means your “active” systems can quietly freeze without throwing a single error.
The wall of red on our actions board
While checking the cron status on my repositories, I opened the blog pipeline and saw the 53 failed builds.
My immediate reaction was confusion. I write posts locally, run a fast development server, check the layout, and push the master branch. The site goes live. How could there be 53 failures?
As it turned out, earlier deployments had succeeded, but a series of quick, mid-sprint edits to my speed test article had introduced a tiny, fatal bug. Because my local dev environment was forgiving, I didn’t see it. But the second the code hit the cloud, the build engine collapsed.
Why Astro is a strict school teacher
The primary culprit behind almost all of my failed builds was how Astro handles asset paths within Markdown content collections.
I had recently upgraded my Mac speed test post to include a custom vector graphic. Locally, I pointed the image source to ./assets/2026-05-22/speedtest-160-mbps.svg. It rendered perfectly in my local workspace.
But when Astro runs a production compile (astro build), it resolves relative assets strictly. If you reference an image with a relative path ./assets/... inside a blog post, Astro’s compiler searches your src/content/blog/ directory for that file. But my assets actually lived in the global public/ folder.
Astro treats a missing or misresolved asset as a fatal build error rather than ignoring it and rendering a broken icon.
Think of Astro’s compiler like an overzealous post-office clerk. If you write the recipient’s address on the back of the envelope in pencil instead of on the front in ink, they bypass any correction requests and throw your entire mailing sack straight into the incinerator.
Because I was pushing edits to the repository and assuming everything was fine, the cloud runner was repeatedly failing on the asset resolution stage.
Fixing the path before the cloud breaks
To resolve this, we had to align our file structure with Astro’s compilation rules.
Images placed in the public/ directory are copied as-is into the root of your built site. Therefore, they must always be referenced using absolute paths starting from the root:
# OLD: Broken relative path

# NEW: Correct absolute path

The second we swapped the relative dot (.) for a leading slash (/), Astro’s compiler was able to build the static pages cleanly. The cloud pipeline went instantly green.
Putting up our own local guardrails
While fixing the asset paths restored our green build status, the real lesson was that depending on the cloud to catch local mistakes is an expensive and slow feedback loop. Waiting 50 seconds for a GitHub Actions runner to tell you that you made a typing error in a Markdown file is a waste of momentum.
To prevent this permanently, Zuri and I built a local gatekeeper — a Git pre-push hook.
We wrote a simple shell script and dropped it into .git/hooks/pre-push inside the blog project:
#!/bin/sh
echo "=== Running local build verification before push ==="
pnpm build
if [ $? -ne 0 ]; then
echo "❌ Error: Local build failed! Push aborted to protect history."
exit 1
fi
We then made the script executable with chmod +x.
Now, the moment I or any agent runs git push, the local system runs a production compile first. If there’s a broken asset path, a typo in the frontmatter, or a syntax glitch, the push is aborted before it ever reaches GitHub. Our remote build history stays clean, and we find out about errors in milliseconds rather than minutes.