From 3a6906a8d793e7a6101e8879ef6a22f4dfa7ac8d Mon Sep 17 00:00:00 2001 From: = Date: Sun, 24 May 2026 23:37:01 -0400 Subject: [PATCH] fix(bitbucket): handle API errors in workspace listing and guard empty pipelineId - Add HTTP status code check in listBitbucketWorkspaces, mirroring the existing check in listBitbucketRepos. A non-2xx Bitbucket response was silently unmarshalled as an empty WorkspaceResponse, causing the UI to show No data to select with no error surfaced to the user. - Guard against calling the pipeline subtasks API with an empty pipelineId in card.tsx and step-4.tsx. The record is initialised with pipelineId="" in step-2 before a pipeline has been triggered, causing the frontend to issue GET /pipelines//subtasks and receive a 400 invalid pipeline ID format error from the backend. Fixes #8868 --- backend/plugins/bitbucket/api/remote_api.go | 9 +++++++++ config-ui/src/routes/onboard/components/card.tsx | 4 ++-- config-ui/src/routes/onboard/step-4.tsx | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/backend/plugins/bitbucket/api/remote_api.go b/backend/plugins/bitbucket/api/remote_api.go index 2da3f7ab626..211901ec3c6 100644 --- a/backend/plugins/bitbucket/api/remote_api.go +++ b/backend/plugins/bitbucket/api/remote_api.go @@ -80,6 +80,15 @@ func listBitbucketWorkspaces( if err != nil { return } + if res.StatusCode > 299 { + body, e := io.ReadAll(res.Body) + if e != nil { + err = errors.BadInput.Wrap(e, "failed to read response body") + return + } + err = errors.HttpStatus(res.StatusCode).New(string(body)) + return + } resBody := &models.WorkspaceResponse{} err = api.UnmarshalResponse(res, resBody) diff --git a/config-ui/src/routes/onboard/components/card.tsx b/config-ui/src/routes/onboard/components/card.tsx index d2250573da3..2d45ffe3eab 100644 --- a/config-ui/src/routes/onboard/components/card.tsx +++ b/config-ui/src/routes/onboard/components/card.tsx @@ -49,11 +49,11 @@ export const OnboardCard = ({ style }: Props) => { const tasksRes = useAutoRefresh( async () => { - if ((data && data.done) || !record) { + if ((data && data.done) || !record || !record.pipelineId) { return; } - return await API.pipeline.subTasks(record?.pipelineId as string); + return await API.pipeline.subTasks(record.pipelineId as string); }, [record], { diff --git a/config-ui/src/routes/onboard/step-4.tsx b/config-ui/src/routes/onboard/step-4.tsx index 569ed0f3da6..eb6eb44ebd4 100644 --- a/config-ui/src/routes/onboard/step-4.tsx +++ b/config-ui/src/routes/onboard/step-4.tsx @@ -112,7 +112,10 @@ export const Step4 = () => { const { data } = useAutoRefresh( async () => { - return await API.pipeline.subTasks(record?.pipelineId as string); + if (!record?.pipelineId) { + return; + } + return await API.pipeline.subTasks(record.pipelineId as string); }, [record], {