writing-github-pr-descriptions
Updating PR Descriptions
Overview
gh pr edit --body can fail silently on some GitHub instances or configurations, accepting the command but not persisting changes. The GitHub REST API provides a reliable fallback when standard CLI tools don't work as expected.
When to Use
gh pr edit --bodyaccepts command but changes don't persist (silent failure)- Need to programmatically update PR description with multiline content
- Working with complex PR templates where edits aren't reflected
- Need guaranteed persistence before moving forward
Not needed when:
gh pr edit --body-filesuccessfully updates your PR- You only need to update title or simple fields
Core Pattern
When gh pr edit doesn't persist:
# Step 1: Get current PR details to verify number
gh pr view --json number -q '.number'
# Step 2: Try standard approach first
gh pr edit <PR_NUMBER> --body "$(cat <<'EOF'
[new body content]
EOF
)"
# Step 3: If no error but changes don't persist, use API directly
gh api repos/OWNER/REPO/pulls/<PR_NUMBER> -X PATCH -f body="$(cat <<'EOF'
[new body content]
EOF
)"
Key Pattern Elements
Use heredoc with <<'EOF' (not <<"EOF")
The single quotes prevent shell expansion:
# ✅ Correct - preserves backticks, $variables, special chars
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$(cat <<'EOF'
Contains `backticks` and $special chars
EOF
)"
# ❌ Wrong - shell expands variables before API call
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$(cat <<"EOF"
Contains `backticks` and $special (expands!)
EOF
)"
Get PR number if unknown
# From current branch (if tracking remote PR)
gh pr view --json number -q '.number'
Verify API call succeeded
Check the returned JSON contains your body:
gh api repos/owner/repo/pulls/123 -X PATCH -f body="new content" | grep -q "new content" && echo "Success"
Implementation
Simple Update
PR_NUMBER=$(gh pr view --json number -q '.number')
gh api repos/Reckon-Limited/reckon-frontend/pulls/$PR_NUMBER -X PATCH -f body="$(cat <<'EOF'
# Summary
This PR implements feature X.
## Features
- Feature A
- Feature B
# Testing
<!-- Testing notes -->
EOF
)"
Update with Verification
PR_NUMBER=$(gh pr view --json number -q '.number')
NEW_BODY="# Summary
This PR implements employee list actions."
gh api repos/Reckon-Limited/reckon-frontend/pulls/$PR_NUMBER -X PATCH -f body="$NEW_BODY"
# Verify by checking the returned body matches
gh pr view $PR_NUMBER --json body -q '.body' | head -1
Common Mistakes
Mistake 1: Using wrong quoting for heredoc
# ❌ Wrong - variables expand, breaks formatting
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$(cat <<"EOF"
Variables like $VAR and $(commands) will expand
EOF
)"
# ✅ Correct
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$(cat <<'EOF'
Variables like $VAR and $(commands) stay literal
EOF
)"
Mistake 2: Not checking return value before assuming success
# ❌ Command returns 200 but changes don't persist
gh pr edit 123 --body "new content" # exits 0, no error
# ✅ Use API and check response
gh api repos/owner/repo/pulls/123 -X PATCH -f body="new content" | grep -q "new content"
Mistake 3: Forgetting --body-file as intermediate step
# ✅ Best practice order:
# 1. Try gh pr edit --body-file (file-based, more reliable than inline)
# 2. If that fails, use gh api (always works)
gh pr edit 123 --body-file my_body.md # Try this first
Mistake 4: Building body string incorrectly with special chars
# ❌ Wrong - newlines lost, special chars cause issues
BODY="# Summary\nNew content"
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$BODY"
# ✅ Correct - use heredoc to preserve formatting
gh api repos/owner/repo/pulls/123 -X PATCH -f body="$(cat <<'EOF'
# Summary
New content
EOF
)"
Troubleshooting
If API call returns 422 Unprocessable Entity:
- Check PR number is correct:
gh pr view --json number - Check repository path (OWNER/REPO):
gh api repos/Reckon-Limited/reckon-frontend --json nameWithOwner - Verify authentication:
gh auth status
If body updates but contains escaped newlines or formatting issues:
- Use heredoc with single quotes:
<<'EOF'not<<"EOF" - Avoid inline strings for complex content - use heredoc
If command succeeds but gh pr view shows old body:
- Wait a moment (GitHub API eventual consistency)
- Clear any local cache:
gh pr view --refresh - Verify via GitHub web UI (browser may cache)
Real-World Impact
Using the API fallback when gh pr edit silently fails ensures PR descriptions are reliably updated in automation scripts, reducing manual follow-up work and ensuring accurate PR documentation for reviewers.
More from zenobi-us/dotfiles
leaflet-mapping
Use when creating interactive maps in Obsidian using LeafletJS plugin - covers real-world maps, image maps, markers from notes, overlays, GeoJSON, GPX tracks, and common issues with bounds/zoom levels
73skill-hunter
Find and download skills. Use when you need to discover existing skills from GitHub repositories and store them in the correct local skills category. Results in discovered skills being downloaded into the users dotfile repo.
68using-superpowers
Use when starting any conversation - establishes mandatory workflows for finding and using skills, including using Skill tool before announcing usage, following brainstorming before coding, and creating TodoWrite todos for checklists
67deep-researcher
Use when delegating research tasks requiring verified information from multiple authoritative sources - crawls and fact-checks beyond surface-level findings, providing evidence of verification process with confidence levels for each claim
66chrome-debug
Use when debugging web applications in chrome via the remote debugging protocol. Provides capabilities for inspecting DOM, executing JS, taking screenshots, and automating browser interactions.
64projectmanagement
Skills for managing projects, tracking progress, and suggesting next actions.
64