gh issue view Kept Failing Because Ubuntu's ESM Outranks GitHub's Repo
TL;DR: Ubuntu 24.04's packaged gh (2.45.0) is broken. GitHub sunset Projects (classic), and 2.45.0 still requests that field in its GraphQL query, so what reads like a deprecation warning is actually a hard error that kills every gh issue view and gh pr view. Three problems stacked: the warning was the error, GitHub's apt repo lives at /packages not /apt, and Ubuntu ESM out-pins GitHub at priority 510 vs 500. Fixed on 2.97.0.
I was trying to read an issue on this site's repo:
gh issue view 28 --repo ManningWorks/lukemanning-site --comments
What came back was this:
GraphQL: Projects (classic) is being deprecated in favor of the new Projects
experience, see: https://github.blog/changelog/2024-05-23-sunset-notice-projects-classic/.
(repository.issue.projectCards)
I asked my AI assistant (opencode) what it meant. It told me it was just a deprecation warning, not an error, and the command works fine.
Except it kept telling me the command had failed. So I pushed back: "You keep telling me that command fails though." It ran the command, and a different error surfaced. This one was mine. In my message I'd written gh issue view 28 --repo --comments with no repo value, assuming it would fill in the blanks. It ran exactly what I'd written. --repo with no value swallowed --comments:
expected the "[HOST/]OWNER/REPO" format, got "--comments"
Fine. My shorthand, my missing value. With the repo filled in, the original failure came straight back:
Exit code 1. Zero bytes on stdout. The deprecation message, alone, on stderr.
That's when it clicked. The "warning" was the failure. The command produced nothing except a note formatted like a heads-up about the future.
The assistant then decided the deprecation message was a red herring that would only appear once the repo resolved. Also wrong. Twice in one conversation.
Problem One: The Warning Was the Error
Ubuntu ships gh 2.45.0. That version requests repository.issue.projectCards as part of its GraphQL query for issue views. GitHub sunset Projects (classic) in May 2024 and now returns that field as a hard GraphQL error. A hard error kills the whole query, so every gh issue view and gh pr view on 2.45.x fails with output that reads like a deprecation notice.
This is a known thing: cli/cli#11992. The maintainers' answer is to stop using distro packages and install from GitHub's official repo. GitHub's Linux install docs now say it outright: as of November 2025 they strongly recommend the official Debian packages, since the community-distributed 2.45.x and 2.46.x are broken against current GitHub APIs.
So Ubuntu's gh is broken in a way GitHub officially acknowledges. Weirdly comforting.
The Workaround
gh api goes through REST, not GraphQL, so it still worked:
gh api repos/ManningWorks/lukemanning-site/issues/28 --jq '.title, .body'
gh api repos/ManningWorks/lukemanning-site/issues/28/comments --jq '.[] | .author.login, .body'
Clunky, but it let me read the issue while I sorted out the upgrade.
Problem Two: The Wrong Repo URL
The assistant set up GitHub's apt repo from memory: https://cli.github.com/apt.
The repository 'https://cli.github.com/apt stable Release' does not have a Release file.
That's a 404. The repo isn't there. I enjoyed this one. The assistant guessed a URL instead of checking the docs, which is the exact thing it tells me not to do constantly.
GitHub's own install docs say it's https://cli.github.com/packages. The assistant followed them this time, keyring staged through /tmp since wget can't write to /etc/apt/keyrings as me:
wget -nv -O /tmp/githubcli-archive-keyring.gpg https://cli.github.com/packages/githubcli-archive-keyring.gpg
cat /tmp/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list
Repo added, apt update clean, apt install gh... nothing. Still 2.45.0. Three failure layers, for anyone counting.
Problem Three: ESM Refuses to Lose
apt-cache policy gh explained it:
gh:
Installed: 2.45.0-1ubuntu0.3+esm3
Candidate: 2.45.0-1ubuntu0.3+esm3
Version table:
2.97.0 500
500 https://cli.github.com/packages stable/main amd64 Packages
*** 2.45.0-1ubuntu0.3+esm3 510
510 https://esm.ubuntu.com/apps/ubuntu noble-apps-security/main amd64 Packages
GitHub's repo offers 2.97.0 at priority 500, the apt default. Ubuntu ESM pins its version at 510. ESM is Expanded Security Maintenance, the security-update stream that comes with Ubuntu Pro, and this machine is Ubuntu 24.04 (noble), amd64, with Pro enabled. Higher priority wins the candidate slot, even though ESM's 2.45.0 is ancient.
An explicit version bypasses the priority contest:
sudo apt install gh=2.97.0
That worked. gh --version reported 2.97.0, and gh issue view 28 --comments (from the repo root, so no --repo needed) finally exited 0 with full output.
The Pin, Validated
The explicit install worked, but the candidate was still ESM's 2.45.0. The assistant said I needed a pin file so GitHub's repo wins going forward. Before touching anything in /etc/apt/preferences.d/, I asked it to validate that. Do Ubuntu or GitHub actually document this, or is it forum folklore?
Three primary sources:
-
My own machine.
/etc/apt/preferences.d/ubuntu-pro-esm-apps, shipped by ubuntu-pro-client, pinsrelease o=UbuntuESMAppsat 510. The file's own comment says it's deliberate: when ESM is enabled, ESM packages are preferred over non-ESM ones. -
man apt_preferences. Priorities from 500 to 990 install a version unless a newer one is already installed. 1000 or higher is required to downgrade. So ESM can't drag me back to 2.45.0, but a plainapt upgradewould never pick up a future GitHub release either. I'd be frozen on 2.97.0. -
Canonical's tracker and docs. ubuntu-pro-client issue #3330 is someone hitting exactly this: a newer fish from a PPA, ignored in favour of ESM's. A Canonical maintainer closed it as intended, because cloud customers didn't want ESM security fixes silently reverted. The stated remedy:
manually configure apt... by creating a preferences file pinning the PPA to a higher version than ESM.
Canonical's Pro Client docs say close to the same: give the third-party repo at least 510.
So the pin is real, and documented. Good.
Why 600 and Not 510
Those two sources don't quite agree, it turns out. The docs allow equal: at least 510. The issue thread says higher than ESM. And what apt actually does when two repos sit at the same priority, I couldn't find in man apt_preferences. Maybe there's a tie-break rule somewhere. I didn't find it.
I let the assistant pick the number in the end, within the bounds the man page had already given me. It went with 600: unconditionally "GitHub's repo is authoritative for gh", above both suggested floors, and below 1000 so nothing can ever downgrade me.
Works for me. 510 would satisfy the docs, but a pin that only ties the thing it's fighting isn't a config I want to reason about again in six months.
/etc/apt/preferences.d/gh:
# gh: prefer GitHub official apt repo over Ubuntu ESM pin (510).
# ESM gh 2.45.x is broken vs current GitHub APIs - see cli/cli#11992.
# Canonical documents this remedy: https://documentation.ubuntu.com/pro-client/en/latest/explanations/about_esm/
Package: gh
Pin: origin cli.github.com
Pin-Priority: 600
Then I verified with apt-cache policy gh, which now shows *** 2.97.0 600. I checked because the assistant had flagged the failure mode to watch for: if the origin in the pin doesn't match the host in the sources list, there's no error. The pin just doesn't apply.
What Stuck
Any one of these three alone would have been a proper rabbit hole.
The assistant was confidently wrong twice, and that's the part I keep relearning. First that the message was harmless, then that it was a red herring. Both times, actually running the command with stdout and stderr separated settled it in seconds. Asking an AI what a command does is asking it to hallucinate from training data. The apt URL guess showed exactly how that goes.
gh works now. Which is nice, because I originally just wanted to read one issue.