The Claude Code team’s write-up on how they run code review reached the Hacker News front page this week, and the discussion underneath it followed a familiar shape: more reviewers, better reviewers, machine reviewers. Almost nobody talked about the oldest and cheapest review control GitHub ships, the one that decides who gets asked in the first place.
A CODEOWNERS file maps paths to the people responsible for them. Touch a matching file, and GitHub requests a review from the owner automatically. Combine it with a protected branch and the owner’s approval becomes mandatory. It is the control our own writing has recommended repeatedly, usually in the same sentence as .github/workflows/.
It also fails silently. An owner who has lost write access is quietly ignored, the review request is never sent, and the pull request merges looking exactly like one that was properly reviewed. So we measured it, using an endpoint GitHub publishes and almost nobody calls.
TL;DR
- Of the 500 most-starred active repositories on GitHub, only 123 (24.6%) have a
CODEOWNERSfile at all. - 32 of those 123 name at least one owner GitHub refuses to resolve. 91 of 284 team references (32.0%) are dead.
- Across 15,505 rules we found zero syntax errors. Every one of the 16,270 faults GitHub reported was an owner that does not resolve, which no linter or reviewer would catch.
- Nine repositories ship a
CODEOWNERSfile that protects no file whatsoever, includingopenai/codex, where all 12 rules point at one unresolvable team. - Ownership is bimodal: with a catch-all rule, median file coverage is 100%; without one, 4.4%. Overall, 35.2% of 1,021,986 files have a reviewer who can actually be asked.
- GitHub will grade your file for free:
GET /repos/{owner}/{repo}/codeowners/errors. It needs no authentication on a public repository.
The endpoint nobody calls
GitHub’s REST API exposes /repos/{owner}/{repo}/codeowners/errors, which returns the faults GitHub itself detects in your CODEOWNERS file. It reports the line, the column, the offending source text and a suggestion. A 404 means there is no CODEOWNERS file at any of the three locations GitHub searches.
This matters because the authority is GitHub, not us. We are not guessing whether an owner has write access. We are asking the system that decides whether to send the review request, and recording its answer.
Our corpus was the 500 most-starred non-archived public repositories, fetched from the search API on 17 October 2026. The smallest has 50,039 stars. These are not neglected projects, and every one of them has more than 1,000 forks, so they all take pull requests from outside.
For each repository we called the errors endpoint, fetched the CODEOWNERS file from .github/, the root, then docs/ in the order GitHub documents, parsed every rule, and pulled the full file tree of the default branch to calculate what fraction of real files each rule set actually covers.
Checking the measurement before trusting it
An obvious objection: perhaps GitHub reports an owner as unknown because our token cannot see a private team. We tested that before going further, and the answer is no.
First, the endpoint is caller-independent. We ran seven repositories authenticated and anonymous, and the error counts were identical every time, including 1,766 for home-assistant/core and 189 for nodejs/node.
Second, and more conclusively, the failures are asymmetric within a single organisation. In grafana/grafana, 28 of 29 referenced teams resolve fine and one does not. In pytorch/pytorch, 62 of 64 named users resolve and two do not. A visibility artefact would take out every team in the org at once. This takes out specific ones, which is exactly what losing write access looks like.
Finding one: three quarters of these repositories have no owners file
Only 123 of 500 (24.6%) have a CODEOWNERS file. Of those, 105 keep it in .github/ and 18 in the repository root. Not one used docs/.
The split by account type is sharp. 102 of 309 organisation-owned repositories have one, against 21 of 191 user-owned. Some of the gap is honest: 46 of the repositories without a file have no primary language at all, because they are awesome-lists and book collections where path ownership means little. Most of the gap is not.
Four more files exist but contain nothing executable. vercel/next.js, at 143,047 stars, ships a CODEOWNERS consisting of two comment lines pointing at a documentation URL that has since moved. tauri-apps/tauri has every rule commented out with a note explaining why. realworld-apps/realworld ships a single byte.
Finding two: every fault is invisible to review
Across the 119 files with at least one rule we counted 15,505 rules, of which 15,474 assign an owner and 31 deliberately remove ownership.
GitHub reported 16,270 faults. Every single one was the same kind: Unknown owner. There was not one syntax error in the entire corpus.
That is the finding underneath the finding. These files are syntactically perfect. They parse, they lint, they read correctly in a pull request diff, and a reviewer scanning the change sees a plausible username next to a plausible path. The failure is entirely in the resolution step, which happens on GitHub’s servers, at merge time, in silence.
32 of the 123 repositories (26.0%) name at least one owner GitHub will not resolve. Split by owner kind, 91 of 284 team references (32.0%) are dead, as are 6,760 of 7,431 user references, though that last figure is dominated by DefinitelyTyped, which lists thousands of package maintainers who were never repository collaborators by design.
Exclude DefinitelyTyped and home-assistant/core as the two bulk-maintainer outliers and the picture barely improves where it counts: 30 of the remaining 117 repositories (25.6%) still have at least one dead owner, covering 382 rules that assign nobody. One in four, either way.
Finding three: nine files protect nothing at all
Combining the rules with the real file trees gives the number that matters, which is not how many rules are broken but how much code ends up with a reviewer who can be asked.
Nine of the 123 repositories have a CODEOWNERS file that covers zero files. Four are the empty ones above. The other five have rules that are all dead.
The clearest case is openai/codex, at 129,568 stars. Its CODEOWNERS is tidy, commented and entirely non-functional: all 12 rules name @openai/codex-core-agent-team, and GitHub resolves none of them. Two of those rules are worth reading slowly:
/.github/workflows/rust-release.yml @openai/codex-core-agent-team
/.github/CODEOWNERS @openai/codex-core-agent-team
The first is the release workflow that handles macOS code signing. The second is the rule that stops somebody quietly editing the ownership file itself. Both were written by people who understood the threat precisely. Neither requests a review from anyone.
nodejs/node is the same shape at scale. It references 34 owners; 33 of them are rejected, with only @nodejs/releasers resolving. That leaves 167 of its 168 rules assigning nobody and, against a tree of 56,468 files, exactly one file with a reviewer who can be asked. jekyll/jekyll has all 61 of its rules dead. tensorflow/tensorflow, at 201,069 stars, is down to 1.0% coverage.
Finding four: the failure mode is drift, not ignorance
grafana/grafana is the most instructive repository in the set, because it is mostly right. It maintains 1,303 rules, nearly all of which resolve, and achieves 99.6% file coverage. Seventeen owner references fail. One of them is this:
/.github/workflows/sbom-on-release.yml @grafana/security
It is the only one of the 29 teams the file names that GitHub will not resolve. Elsewhere in the same file, @grafana/security-team resolves correctly and guards a different workflow. One team name works and a near-identical one does not. The rule that was supposed to put the security team in front of changes to the SBOM release workflow has been inert, and nothing in the repository reports it.
This is the pattern across the whole corpus. Teams get renamed, merged or made secret. Maintainers move on and their write access is revoked. The CODEOWNERS entry does not move with them, because it is a text file that nothing validates on push. ohmyzsh/ohmyzsh has 14 of 19 rules dead for exactly this reason, as plugin maintainers drifted away over the years.
Finding five: you either own everything or you own almost nothing
Across 117 repositories with complete file trees, covering 1,021,986 files, rules nominally cover 47.4% of files. Counting only rules whose owners GitHub accepts, real coverage is 35.2%.
The distribution is not a spread, it is two clusters, and the dividing line is a single character. The 48 repositories with a catch-all * rule have a median coverage of 100%. The 69 without one have a median of 4.4%. Forty of the 117 sit below 10%.
This is the quiet consequence of path-specific ownership. Teams write careful rules for the directories they care about, every one of which works, and leave the default case undefined. Any file nobody thought to name has no owner, which in practice means the newest and least familiar code in the repository is the code with no required reviewer.
On the specific question our own posts keep raising, the results are better but still thin. Of the 119 repositories with real rules, 43 have an explicit, working rule covering .github/workflows/. Another 36 are covered only incidentally by a catch-all. Two name an owner who is dead, and 38 have no rule that matches CI configuration at all.
Why this survives so long
Three documented behaviours combine badly. GitHub silently ignores an owner without write access rather than failing the check. Code owners are not requested on draft pull requests, so a team that drafts first sees nothing unusual. And if a file over 3 MB is committed, GitHub does not load it at all, though no repository in our corpus came close; the largest was 651 KB and the median just 515 bytes.
The result is a control with no failure signal. Required approval from code owners is enforced per file, so when no owner resolves for a path, there is no owner to require, and the merge proceeds. Your branch protection is on. Your rule is written. Nobody is asked.
What to do about it, in about ten minutes
- Call the endpoint today.
curl -s https://api.github.com/repos/OWNER/REPO/codeowners/errorsreturns a JSON list of faults for any public repository, with no token required. A non-emptyerrorsarray means rules in your file are inert right now. - Put the check in CI. Fail the build when the array is non-empty. This is a handful of lines and it converts a silent, permanent fault into a visible, immediate one.
- Add a catch-all. A single
*line naming a default team moves a repository from the 4.4% cluster to the 100% cluster. Place it first, since the last matching rule wins. - Own your CI configuration explicitly. A rule on
/.github/workflows/is the highest-value line in the file, because workflow changes execute with repository credentials. Thirty-eight of these repositories have no such rule. - Verify teams, not just usernames. Teams are the main failure mode at 32%, and a team must exist, be visible and hold write access. Membership alone is not enough.
- Re-run it after every reorganisation. Every dead entry we found was correct on the day it was written.
The pattern underneath
We keep finding the same class of defect: a control that is configured, documented, believed in, and structurally incapable of firing. It was the review gate that referenced a field which is always null on the triggering event. It was the Pod Security Standard that 66 of 389 Helm charts fail by omission, and the 7,802 workflow references pinned to a tag that moves.
What they share is that nothing tells you. The dashboard is green because the dashboard measures whether the rule exists, not whether it can fire. The only defence is to measure the behaviour rather than the configuration, which usually turns out to be cheaper than it sounds. This entire study cost a few hundred API calls against an endpoint that has been public for years.
At REPTILEHAUS we build and audit this layer for clients: review architecture and branch protection that holds up at machine-generated pull request volume, CI and DevOps pipelines where the guards actually engage, and the unglamorous verification work that tells you which of your controls are real. If you have a CODEOWNERS file you have not checked, start with the curl command above. If you would rather we went through the whole estate, get in touch.
Method: 500 most-starred non-archived public repositories via the GitHub search API, 17 October 2026. Faults are as reported by GitHub’s own codeowners/errors endpoint, verified caller-independent. Pattern matching implements the gitignore subset GitHub documents for CODEOWNERS, with last-match-wins precedence. Coverage figures exclude two repositories whose file trees exceeded the API’s recursive limit.
📷 Photo by grey wight on Unsplash

