Skip to content

[bug] Gitea sync fails with ERR_STREAM_PREMATURE_CLOSE, then crashes on null full_name #1404

Description

@HueskenNiels-hue

Describe the bug

Bug description

When syncing repositories from a self-hosted Gitea instance, Sourcebot fails the connection sync job with:

Cannot read properties of null (reading 'full_name')

After debugging, the root cause appears to be that gitea-js / cross-fetch fails while reading the Gitea API response body with:

ERR_STREAM_PREMATURE_CLOSE

This causes api.repos.repoGet() to return:

{
  "size": 0,
  "timeout": 0,
  "data": null,
  "error": {
    "message": "Invalid response body while trying to fetch http://xxxxx:3001/api/v1/repos/xxxx/xxx.xxxx: Premature close",
    "type": "system",
    "errno": "ERR_STREAM_PREMATURE_CLOSE",
    "code": "ERR_STREAM_PREMATURE_CLOSE"
  }
}

Sourcebot then executes this code in /app/packages/backend/dist/gitea.js:

return {
    type: 'valid',
    data: [response.data]
};

Since response.data is null, allRepos contains null. Later this crashes:

allRepos = allRepos.filter(repo => repo.full_name !== undefined);

To reproduce

Inside the Sourcebot container:

import { giteaApi } from "gitea-js";
import fetch from "cross-fetch";

const api = giteaApi("http://gitea:3001", {
  token: process.env.GITEA_TOKEN,
  customFetch: fetch,
});

const response = await api.repos.repoGet("Org", "Repo");

console.log(JSON.stringify(response, null, 2));
{
  "size": 0,
  "timeout": 0,
  "data": null,
  "error": {
    "message": "Invalid response body while trying to fetch http://gitea:3001/api/v1/repos/Fairparken/Fairparken.Worker: Premature close",
    "type": "system",
    "errno": "ERR_STREAM_PREMATURE_CLOSE",
    "code": "ERR_STREAM_PREMATURE_CLOSE"
  }
}

Sourcebot deployment information

Sourcebot version (e.g. v3.0.1): V5.0.4
Gittea image version: docker.gitea.com/gitea:1.26.1-rootless

Additional information

Temporary workaround tested:

I patched /app/packages/backend/dist/gitea.js inside the running Sourcebot container to use a custom fetch:

const customFetch = (url, options = {}) => {
    const headers = {
        ...(options.headers ?? {}),
        'Accept-Encoding': 'identity',
        'Connection': 'close',
    };

    return fetch(url, {
        ...options,
        headers,
    });
};

const api = giteaApi(config.url ?? 'https://gitea.com', {
    token: token,
    customFetch,
});

I also added a null guard:

allRepos = allRepos.filter((repo, index) => {
    if (repo === null || repo === undefined) {
        logger.warn(`Skipping null/undefined Gitea repo at index ${index}`);
        return false;
    }
    return repo.full_name !== undefined;
});

Relevant code locations

/app/packages/backend/dist/gitea.js:42
/app/packages/backend/dist/gitea.js:161-166
/app/packages/backend/dist/repoCompileUtils

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions