with one click
github-actions-log-debugging
// Inspect GitHub Actions job failures and logs without gh CLI, including the signed-blob redirect behavior on the logs endpoint.
// Inspect GitHub Actions job failures and logs without gh CLI, including the signed-blob redirect behavior on the logs endpoint.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | github-actions-log-debugging |
| description | Inspect GitHub Actions job failures and logs without gh CLI, including the signed-blob redirect behavior on the logs endpoint. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["GitHub","Actions","CI","Debugging","Logs"],"related_skills":["github-pr-workflow","github-code-review"]}} |
Use when a PR/job/check failed and gh is unavailable or inconvenient.
GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs often returns a 302 redirect to a signed blob-storage URL.
Important consequence:
AuthorizationAuthorization headercurl -L usually handles this correctly. Custom Python/urllib code may need manual redirect handling.
.../actions/runs/<run_id>/job/<job_id>?pr=<pr>Use the job API first to learn which step failed.
curl -s \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$OWNER/$REPO/actions/jobs/$JOB_ID"
Look at:
statusconclusionsteps[].namesteps[].conclusioncurl -L -s \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$OWNER/$REPO/actions/jobs/$JOB_ID/logs"
If redirect auth handling is broken, do it manually:
Location header from the 302Sketch:
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
opener = urllib.request.build_opener(NoRedirect)
req = urllib.request.Request(api_logs_url, headers=github_headers)
try:
opener.open(req)
except urllib.error.HTTPError as e:
signed_url = e.headers['Location']
with urllib.request.urlopen(signed_url) as r:
text = r.read().decode('utf-8', errors='replace')
Distinguish these cases:
For deploy workflows, always check whether the deploy step itself succeeded before blaming tokens.
Read the workflow file and inspect:
|| true or curl -sThis often explains why logs look sparse.
If logs show:
wrangler deploy succeededNot ready yet / empty health responsesHealth checkThen root cause is not missing Cloudflare credentials. It's a post-deploy readiness/health-check failure (route propagation, app not ready, endpoint not responding, or poor observability in the curl command).
If health-check logs are uninformative, patch workflow commands to surface the failure mode:
curl -Ssv during debugging