500 error on Projects get/list API when a project repository is missing

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

I'm using GitLab 14.0.12-ce, and I have some projects which are missing their repository, despite the git repository not being an explicitly disabled feature: no-repository I don't know why yet, but that's not the point here.

When I try to list all Projects from this server (here using python-gitlab CLI), I get a 500 error on pages which include such projects:

$ gitlab --debug project list --order-by id --sort asc --per-page 10 --page 83
send: b'GET /api/v4/projects?order_by=id&sort=asc&page=83&per_page=10 HTTP/1.1\r\nHost: XXXXXXXXXXXXX\r\nUser-Agent: python-gitlab/2.10.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nPRIVATE-TOKEN: XXXXXXXXXXXXX\r\nContent-type: application/json\r\n\r\n'
reply: 'HTTP/1.1 500 Internal Server Error\r\n'
header: server: nginx
header: date: Wed, 15 Dec 2021 23:37:17 GMT
header: content-type: application/json
header: content-length: 39
header: cache-control: no-cache
header: vary: Origin
header: x-request-id: XXXXXXXXXXXXX
header: x-runtime: 0.252102
header: strict-transport-security: max-age=63072000
DEBUG:urllib3.connectionpool:https://XXXXXXXXXXXXX:443 "GET /api/v4/projects?order_by=id&sort=asc&page=83&per_page=10 HTTP/1.1" 500 39
Impossible to list objects (500: 500 Internal Server Error)

Same if I try to get one single such projects:

$ gitlab --debug project get --id 1907
send: b'GET /api/v4/projects/1907 HTTP/1.1\r\nHost: XXXXXXXXXXXXX\r\nUser-Agent: python-gitlab/2.10.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nPRIVATE-TOKEN: XXXXXXXXXXXXX\r\nContent-type: application/json\r\n\r\n'
reply: 'HTTP/1.1 500 Internal Server Error\r\n'
header: server: nginx
header: date: Wed, 15 Dec 2021 23:40:50 GMT
header: content-type: application/json
header: content-length: 39
header: cache-control: no-cache
header: vary: Origin
header: x-request-id: XXXXXXXXXXXXX
header: x-runtime: 0.057094
header: strict-transport-security: max-age=63072000
DEBUG:urllib3.connectionpool:https://XXXXXXXXXXXXX:443 "GET /api/v4/projects/1907 HTTP/1.1" 500 39
Impossible to get object (500: 500 Internal Server Error)

The exceptions log shows the relevant stack, something like this:

{
  "severity": "ERROR",
  "time": "2021-12-15T08:26:25.125Z",
  "correlation_id": "XXXXXXXXXXXXX",
  "exception.class": "TypeError",
  "exception.message": "no implicit conversion of nil into String",
  "exception.backtrace": [
    "app/models/project.rb:1319:in `join'",
    "app/models/project.rb:1319:in `readme_url'",
    "lib/gitlab/metrics/instrumentation.rb:160:in `block in readme_url'",
    "lib/gitlab/metrics/method_call.rb:27:in `measure'",
    "lib/gitlab/metrics/instrumentation.rb:160:in `readme_url'",
    "lib/gitlab/json.rb:110:in `dump'",
    "lib/gitlab/json.rb:110:in `adapter_dump'",
    "lib/gitlab/json.rb:42:in `dump'",
    "lib/gitlab/json.rb:198:in `call'",
    "lib/api/api_guard.rb:213:in `call'",
    "lib/gitlab/metrics/elasticsearch_rack_middleware.rb:16:in `call'",
    "lib/gitlab/middleware/rails_queue_duration.rb:33:in `call'",
    "lib/gitlab/metrics/rack_middleware.rb:16:in `block in call'",
    "lib/gitlab/metrics/web_transaction.rb:21:in `run'",
    "lib/gitlab/metrics/rack_middleware.rb:16:in `call'",
    "lib/gitlab/middleware/speedscope.rb:13:in `call'",
    "lib/gitlab/request_profiler/middleware.rb:17:in `call'",
    "lib/gitlab/jira/middleware.rb:19:in `call'",
    "lib/gitlab/middleware/go.rb:20:in `call'",
    "lib/gitlab/etag_caching/middleware.rb:21:in `call'",
    "lib/gitlab/middleware/multipart.rb:172:in `call'",
    "lib/gitlab/middleware/read_only/controller.rb:50:in `call'",
    "lib/gitlab/middleware/read_only.rb:18:in `call'",
    "lib/gitlab/middleware/same_site_cookies.rb:27:in `call'",
    "lib/gitlab/middleware/handle_malformed_strings.rb:21:in `call'",
    "lib/gitlab/middleware/basic_health_check.rb:25:in `call'",
    "lib/gitlab/middleware/handle_ip_spoof_attack_error.rb:25:in `call'",
    "lib/gitlab/middleware/request_context.rb:21:in `call'",
    "config/initializers/fix_local_cache_middleware.rb:11:in `call'",
    "lib/gitlab/middleware/rack_multipart_tempfile_factory.rb:19:in `call'",
    "lib/gitlab/metrics/requests_rack_middleware.rb:74:in `call'",
    "lib/gitlab/middleware/release_env.rb:12:in `call'"
  ],
  "user.username": "root",
  "tags.program": "web",
  "tags.locale": "en",
  "tags.feature_category": "projects",
  "tags.correlation_id": "XXXXXXXXXXXXX"
}

I know I'm using a rather old GitLab version (14.0.x), but I don't think this has been fixed yet:

https://gitlab.com/gitlab-org/gitlab/-/blob/921fd6ab924df5e0dba6fff7cdb88e1d1ad2f719/app/models/project.rb#L1351

I'm not a Ruby dev', but my understanding is that the case of default_branch being nil is not handled. Probably it should end up with readme_url returning nil too, like when there is no repository.readme_path.

Edited by 🤖 GitLab Bot 🤖