You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2026/06/30 09:37:21 UTC

(devlake) branch main updated: fix(azuredevops): treat 204 No Content as graceful skip in timeline collector (#8958)

This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new c43696ab5 fix(azuredevops): treat 204 No Content as graceful skip in timeline collector (#8958)
c43696ab5 is described below

commit c43696ab5c67e509b7119ac0ccb15ec2ff64adee
Author: bujjibabukatta <bu...@gmail.com>
AuthorDate: Tue Jun 30 15:07:15 2026 +0530

    fix(azuredevops): treat 204 No Content as graceful skip in timeline collector (#8958)
---
 backend/plugins/azuredevops_go/tasks/shared.go      | 7 +++++--
 backend/plugins/azuredevops_go/tasks/shared_test.go | 6 ++++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/backend/plugins/azuredevops_go/tasks/shared.go b/backend/plugins/azuredevops_go/tasks/shared.go
index e6fe0eca1..318850077 100644
--- a/backend/plugins/azuredevops_go/tasks/shared.go
+++ b/backend/plugins/azuredevops_go/tasks/shared.go
@@ -153,8 +153,11 @@ func change203To401(res *http.Response) errors.Error {
 // that failed due to a YAML syntax error never produce a usable timeline), instead
 // of aborting the entire subtask.
 func ignoreInvalidTimelineResponse(res *http.Response) errors.Error {
-	// Keep existing behaviour: treat 404 as a graceful skip (build was deleted).
-	if res.StatusCode == http.StatusNotFound {
+	// Treat 404 (build deleted) and 204 (build has no timeline data) as
+	// graceful skips so the subtask continues instead of failing.
+	// The 204 guard must come before the body is read because a 204 response
+	// has an empty body by definition and would cause the JSON parser to fail.
+	if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusNoContent {
 		return api.ErrIgnoreAndContinue
 	}
 
diff --git a/backend/plugins/azuredevops_go/tasks/shared_test.go b/backend/plugins/azuredevops_go/tasks/shared_test.go
index 353ae3f4c..d46e01d6c 100644
--- a/backend/plugins/azuredevops_go/tasks/shared_test.go
+++ b/backend/plugins/azuredevops_go/tasks/shared_test.go
@@ -41,6 +41,12 @@ func TestIgnoreInvalidTimelineResponse_404(t *testing.T) {
 	assert.Equal(t, api.ErrIgnoreAndContinue, err, "404 should return ErrIgnoreAndContinue")
 }
 
+func TestIgnoreInvalidTimelineResponse_204(t *testing.T) {
+	res := makeResponse(http.StatusNoContent, "")
+	err := ignoreInvalidTimelineResponse(res)
+	assert.Equal(t, api.ErrIgnoreAndContinue, err, "204 No Content should return ErrIgnoreAndContinue")
+}
+
 func TestIgnoreInvalidTimelineResponse_EmptyBody(t *testing.T) {
 	res := makeResponse(http.StatusOK, "")
 	err := ignoreInvalidTimelineResponse(res)