Hash collisions in GraphQL aliases in success lifecycle

I ran into an interesting failure in our CI pipeline when the success lifecycle of the semantic-release/github plugin ran the other day. It didn't block our release but left the PR and issue updates incomplete. The error surfaced was:

Error: Field 'commit429fa6' has an argument conflict: {oid:"/"429fa60a84f7b59f9e4a20bfbea6b0fa977a1e1e/""} or {oid:"/"429fa667b462b60b31769e5268cfbc6835793c14/""}

This occurred due to two different commits within the release we were making sharing the same first 6 characters of their SHA (429fa6). It turns out the query builder, which builds the query to pass to GitHub's API, uses the first 6 characters of the SHA to create GraphQL aliases:

commit${sha.slice(0, 6)}: object(oid: "${sha}") { ... }

This code is located at Line 460 in success.js.

Our CI pipeline failure

Background

This behaviour was introduced in PR #857, which rewrote the success lifecycle to use GitHub’s GraphQL API for better rate-limit handling.

The intention behind truncating to 6 characters was likely to keep aliases short and readable, but it introduces collisions in real-world repositories. As commits within a repo increase there is a higher likelihood of collision due to the birthday paradox effect.

In our case, we had a number of rapid changes occur and a larger than normal amount of commits generated that all wound up in the same release.

I ran some analysis on our repository of 2,724 commits (at the time of writing) and discovered we have two collision pairs of commits when considering the first 6 characters of the SHA. The first collision pair contains two commits two years apart. The offending collision pair that surfaced this issue contains two commits a few weeks apart.

Impact

  • GraphQL requires unique aliases for fields with different arguments.
  • When two commits share the same prefix, the query fails with an argument conflict error from the GitHub GraphQL API.
  • This breaks the success lifecycle and prevents associated PRs from being fetched.

Proposed Fix

Use a longer short SHA prefix, ≥7–12 chars. GitHub uses 7 digit short SHAs, however the longer the slice the smaller the collision space is.