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

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions