Inconsistent handling of moved repositories using API v4

It seems the GraphQL API returns new repository location information only if the former location’s user still exists.

For instance, using my browser, I can access old repositories and be redirected to the new location:

https://github.com/jim/carmenhttps://github.com/carmen-ruby/carmen

http://github.com/mjijackson/rack-accepthttps://github.com/mjackson/rack-accept

Which would indicate in both cases the moving of the repository has been done “properly” (e.g. not by creating a brand new user and git push there), allowing Github to know where to redirect to.

However in GraphQL the handling is different:

query {
  repository(owner: "jim", name: "carmen") {
    nameWithOwner
  }
}

{
  "data": {
    "repository": {
      "nameWithOwner": "carmen-ruby/carmen"
    }
  }
}

query {
  repository(owner: "mjijackson", name: "rack-accept") {
    nameWithOwner
  }
}

{
  "data": {
    "repository": null
  },
  "errors": [
    {
      "type": "NOT_FOUND",
      "path": [
        "repository"
      ],
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "message": "Could not resolve to a User with the username 'mjijackson'."
    }
  ]
}

I assume this is a known limitation?

Are there any plans to make this more consistent?

With the current situation I have to fallback to the v3 API  just for such cases, sending more requests than it should be and having to maintain this extra logic…

1 Like

Thanks for reaching out.

I’ve passed this along to the developers who are investigating. We’ll update you when we get more information.

Thanks for letting us know!

1 Like

@lee-dohm any update on this? It’s been a year now and as far as I can tell, this is still an issue.

Unfortunately, no, there have been no updates on this.

Bummer. I’ll retry in 2021…

You should not be using the repository(owner: String!, name: String!) method for any resource you plan to query subsequently. You should always get the id of the repository and subsequent queries use the ID through node or nodes. That way your code is robust to renaming / transfers.

I’m not sure I understand, how do you get a repo id in the first place? The only info I have at my disposal to query is the repo owner and repo name, however outdated those may be.

Say you have some repositories you want to track based on the slug.
You have something like the following GraphQL query

query magic($owner1: String!, $name1: String!, $owner2: String!, $name2: String!) {
  _1: repository(owner: $owner1, name: $name1) {
    id
  }
  _2: repository(owner: $owner2, name: $name2) {
    id
  }
}

with variables

{
  "owner1": "carmen-ruby",
  "name1": "carmen",
  "owner2": "mjackson",
  "name2": "rack-accept"
}

which return

{
  "data": {
    "_1": {
      "id": "MDEwOlJlcG9zaXRvcnkxNTI5ODc="
    },
    "_2": {
      "id": "MDEwOlJlcG9zaXRvcnk1OTA4NTA="
    }
  }
}

if the repositories are ever renamed or transferred you can always get the new location through

query magic($ids: [ID!]!) {
  nodes(ids: $ids) {
    ... on Node {
      ... on Repository {
        nameWithOwner
      }
    }
  }
}

and query variables

{
  "ids": ["MDEwOlJlcG9zaXRvcnkxNTI5ODc=", "MDEwOlJlcG9zaXRvcnk1OTA4NTA="]
}

for the following payload

{
  "data": {
    "nodes": [
      {
        "nameWithOwner": "carmen-ruby/carmen"
      },
      {
        "nameWithOwner": "mjackson/rack-accept"
      }
    ]
  }
}

That should get you what you want moving forward. If you just need to get the “redirect” so you can adapt the code to use the global node ID you can use any HTTP library to get the redirect; no need to use the GitHub API for that. For example using HTTP.jl,

using HTTP: request
"""
    magic(slug::AbstractString)

Return current slug of the repository or `missing` if `NOT_FOUND`.
"""
function magic(slug::AbstractString)
    try
        response = request("GET", "https://github.com/$slug")
        response.request.target[2:end]
    catch err
        err.status ≠ 404 && throw(err)
        missing
    end
end
magic("jim/carmen")
"carmen-ruby/carmen"

Let me know if that helps.

Thanks for the thorough reply!

The problem I face though is I have no control whatsoever of the owner/repo name pairs I’m going to get. And while the v3 API handles just fine moved repos, the issue here is v4 falls flat on its face in the one case where a repo moved and the former owner account doesn’t exist anymore.
When used with the old owner of rack-accept, “mjijackson”, not “mjackson”, the query to get the repo IDs fails all the same:

query magic($owner1: String!, $name1: String!, $owner2: String!, $name2: String!) {
  _1: repository(owner: $owner1, name: $name1) {
    id
  }
  _2: repository(owner: $owner2, name: $name2) {
    id
  }
}
{
  "owner1": "carmen-ruby",
  "name1": "carmen",
  "owner2": "mjijackson",
  "name2": "rack-accept"
}
{
  "data": {
    "_1": {
      "id": "MDEwOlJlcG9zaXRvcnkxNTI5ODc="
    },
    "_2": null
  },
  "errors": [
    {
      "type": "NOT_FOUND",
      "path": [
        "_2"
      ],
      "locations": [
        {
          "line": 5,
          "column": 3
        }
      ],
      "message": "Could not resolve to a User with the username 'mjijackson'."
    }
  ]
}

This is definitely a bug affecting the GraphQL API and it’s a shame it’s been sitting on the back burner for more than a year.
Granted, those cases might be rare but regardless of occurence, I still have to maintain code to access the v3 API just for that…

The solution is use the global node ID which is the recommended practice for all resources that one has to query subsequently. When passing the repository by owner/name it first has to do a scan to find the node ID which incurs in wasteful computing and time. I would probably first do a GET request to get the latest slug for the repo and right after get the global node (you could do it with the GitHub RESTful v3 API as well and get the node ID from that call too). Remember that the limits for the REST and GraphQL API are separate so doing a few REST API calls does not affect your GraphQL remaining points.

Thanks for your patience folks, this should now be fixed.

1 Like

Do you mean fixed and released or fixed and soon-to-be-released? Because I still get the same error I described in the original post.

It seems it now redirects on the name so it will work for

jim/carmen -> carmen-ruby/carmen

but still errors out if the owner changed the name

query magic($owner1: String!, $name1: String!, $owner2: String!, $name2: String!) {
  _1: repository(owner: $owner1, name: $name1) {
    id
    nameWithOwner
  }
  _2: repository(owner: $owner2, name: $name2) {
    id
    nameWithOwner
  }
}

with

{
  "owner1": "jim",
  "name1": "carmen",
  "owner2": "mjijackson",
  "name2": "rack-accept"
}

yields

{
  "data": {
    "_1": {
      "id": "MDEwOlJlcG9zaXRvcnkxNTI5ODc=",
      "nameWithOwner": "carmen-ruby/carmen"
    },
    "_2": null
  },
  "errors": [
    {
      "type": "NOT_FOUND",
      "path": [
        "_2"
      ],
      "locations": [
        {
          "line": 6,
          "column": 3
        }
      ],
      "message": "Could not resolve to a User with the username 'mjijackson'."
    }
  ]
}

Would be good if the “fix” could be extended to redirect usernames by login and consequently fully match the HTTP GET behavior.

The fix deployed yesterday, I’ve just ran the query and you are absolutely correct. Let me talk to the team.

Thanks for your patience @nosferican, turns out we had to revert this change due to some other issues. The team is working on a fix to ship this once again, I’ll keep you posted here.

1 Like

Will be working on an application for which it would be wonderful to have this functionality by then… I will probably get to it around mid August.

1 Like

Hi @nosferican give this a go please, it should be okay now as the latest fix has been deployed :crossed_fingers:t4:

Confirmed it’s fixed so marking this as resolved. Thanks for following up @andreagriffiths11 !!!

1 Like