From 32a2c9ae17f6af3a186154e9e9e3050c9883f2d0 Mon Sep 17 00:00:00 2001 From: rachit367 Date: Thu, 2 Jul 2026 14:01:13 +0530 Subject: [PATCH 1/2] fix(backend): don't crash Gitea sync when a repo fetch returns a null body When repoGet fails mid-body (e.g. ERR_STREAM_PREMATURE_CLOSE) gitea-js resolves with { data: null, error } instead of throwing. getRepos wrapped that as [null], which flowed into allRepos and crashed the whole sync at allRepos.filter(repo => repo.full_name !== undefined) on null.full_name. Treat a null response body as a per-repo warning (like the 404 path) so a single failed fetch is skipped instead of failing the connection, and harden the top-level filter against null. Also drops a redundant duplicate full_name filter. Fixes #1404 --- packages/backend/src/gitea.test.ts | 58 ++++++++++++++++++++++++++++++ packages/backend/src/gitea.ts | 14 ++++++-- 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/gitea.test.ts diff --git a/packages/backend/src/gitea.test.ts b/packages/backend/src/gitea.test.ts new file mode 100644 index 000000000..a456f9be0 --- /dev/null +++ b/packages/backend/src/gitea.test.ts @@ -0,0 +1,58 @@ +import { expect, test, vi, beforeEach } from 'vitest'; + +const repoGet = vi.fn(); + +vi.mock('gitea-js', () => ({ + giteaApi: () => ({ + repos: { repoGet }, + orgs: { orgListRepos: vi.fn() }, + users: { userListRepos: vi.fn() }, + }), +})); + +vi.mock('cross-fetch', () => ({ default: vi.fn() })); + +vi.mock('@sourcebot/shared', async (importOriginal) => ({ + ...(await importOriginal()), + getTokenFromConfig: vi.fn(async () => 'token'), +})); + +import { getGiteaReposFromConfig } from './gitea'; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +test('a repo whose fetch returns a null body is skipped with a warning instead of crashing', async () => { + repoGet.mockResolvedValue({ + data: null, + error: { message: 'Premature close', code: 'ERR_STREAM_PREMATURE_CLOSE' }, + }); + + const result = await getGiteaReposFromConfig({ + type: 'gitea', + url: 'https://gitea.example.com', + token: { env: 'GITEA_TOKEN' }, + repos: ['org/broken-repo'], + } as never); + + expect(result.repos).toHaveLength(0); + expect(result.warnings.some(w => w.includes('broken-repo'))).toBe(true); +}); + +test('a repo with a valid body is returned', async () => { + repoGet.mockResolvedValue({ + data: { id: 1, full_name: 'org/good-repo' }, + error: null, + }); + + const result = await getGiteaReposFromConfig({ + type: 'gitea', + url: 'https://gitea.example.com', + token: { env: 'GITEA_TOKEN' }, + repos: ['org/good-repo'], + } as never); + + expect(result.repos).toHaveLength(1); + expect(result.repos[0].full_name).toBe('org/good-repo'); +}); diff --git a/packages/backend/src/gitea.ts b/packages/backend/src/gitea.ts index ac7566ee8..e8dd2ee92 100644 --- a/packages/backend/src/gitea.ts +++ b/packages/backend/src/gitea.ts @@ -49,10 +49,9 @@ export const getGiteaReposFromConfig = async (config: GiteaConnectionConfig) => allWarnings = allWarnings.concat(warnings); } - allRepos = allRepos.filter(repo => repo.full_name !== undefined); allRepos = allRepos.filter(repo => { - if (repo.full_name === undefined) { - logger.warn(`Repository with undefined full_name found: repoId=${repo.id}`); + if (repo === null || repo === undefined || repo.full_name === undefined) { + logger.warn(`Skipping Gitea repository with missing data: repoId=${repo?.id}`); return false; } return true; @@ -208,6 +207,15 @@ const getRepos = async (repoList: string[], api: Api) => { api.repos.repoGet(owner, repoName), ); + if (!response.data) { + const warning = `Failed to fetch repository ${repo}: ${response.error?.message ?? 'empty response body'}`; + logger.warn(warning); + return { + type: 'warning' as const, + warning + }; + } + logger.debug(`Found repo ${repo} in ${durationMs}ms.`); return { type: 'valid' as const, From 5edbe5451cca60eceec745b13823f298cc28c4a4 Mon Sep 17 00:00:00 2001 From: rachit367 Date: Thu, 2 Jul 2026 14:01:59 +0530 Subject: [PATCH 2/2] docs: add changelog entry for Gitea null-body sync fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea80d6c5e..b4716a1ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Maintained the sidebar scroll position when navigating between chats instead of resetting to the top. [#1411](https://github.com/sourcebot-dev/sourcebot/pull/1411) - Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356) - Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413) +- Prevented a Gitea connection sync from crashing when a single repository fetch returns an empty response body; the repo is now skipped with a warning. [#1416](https://github.com/sourcebot-dev/sourcebot/pull/1416) ## [5.0.4] - 2026-06-18