@@ -166,7 +166,10 @@ type NoArtifactError struct {
166166}
167167
168168func (n * NoArtifactError ) Error () string {
169- return fmt .Sprintf ("client_error: %s" , n .err .Error ())
169+ if n .err == nil {
170+ return "no artifact found"
171+ }
172+ return fmt .Sprintf ("no artifact found: %s" , n .err .Error ())
170173}
171174
172175func (n * NoArtifactError ) Unwrap () error {
@@ -262,35 +265,39 @@ func (c *Client) PostOne(ctx context.Context, record *DeploymentRecord) error {
262265 }
263266
264267 // Drain and close response body to enable connection reuse by reading body for error logging
265- body , _ := io .ReadAll (io .LimitReader (resp .Body , 4096 ))
268+ respBody , _ := io .ReadAll (io .LimitReader (resp .Body , 4096 ))
266269 _ , _ = io .Copy (io .Discard , resp .Body )
267270 _ = resp .Body .Close ()
268271
269272 switch {
270- case resp .StatusCode == 429 :
271- // Retry with backoff
272- dtmetrics .PostDeploymentRecordSoftFail .Inc ()
273- slog .Debug ("rate limited, retrying" ,
274- "attempt" , attempt ,
275- "status_code" , resp .StatusCode ,
276- )
277- lastErr = fmt .Errorf ("rate limited, attempt %d" , attempt )
278- continue
279- case resp .StatusCode == 404 :
273+ case resp .StatusCode == 404 && bytes .Contains (respBody , []byte ("no artifacts found" )):
280274 // No artifact found
281- dtmetrics .PostDeploymentRecordNotSaved .Inc ()
275+ dtmetrics .PostDeploymentRecordNoAttestation .Inc ()
282276 slog .Debug ("no artifact attestation found, no record created" ,
283277 "attempt" , attempt ,
284278 "status_code" , resp .StatusCode ,
285279 )
286- return & NoArtifactError {err : fmt . Errorf ( "no artifact found" ) }
280+ return & NoArtifactError {}
287281 case resp .StatusCode >= 400 && resp .StatusCode < 500 :
282+ if resp .Header .Get ("retry-after" ) != "" || resp .Header .Get ("x-ratelimit-remaining" ) == "0" {
283+ // rate limited — retry with backoff
284+ // Could be 403 or 429
285+ dtmetrics .PostDeploymentRecordRateLimited .Inc ()
286+ slog .Warn ("rate limited, retrying" ,
287+ "attempt" , attempt ,
288+ "status_code" , resp .StatusCode ,
289+ "retry_after" , resp .Header .Get ("Retry-After" ),
290+ "resp_msg" , string (respBody ),
291+ )
292+ lastErr = fmt .Errorf ("rate limited, attempt %d" , attempt )
293+ continue
294+ }
288295 // Don't retry non rate limiting client errors
289296 dtmetrics .PostDeploymentRecordClientError .Inc ()
290297 slog .Warn ("client error, aborting" ,
291298 "attempt" , attempt ,
292299 "status_code" , resp .StatusCode ,
293- "resp_msg" , string (body ),
300+ "resp_msg" , string (respBody ),
294301 )
295302 return & ClientError {err : fmt .Errorf ("unexpected client err with status code %d" , resp .StatusCode )}
296303 default :
@@ -299,7 +306,7 @@ func (c *Client) PostOne(ctx context.Context, record *DeploymentRecord) error {
299306 slog .Debug ("retriable error" ,
300307 "attempt" , attempt ,
301308 "status_code" , resp .StatusCode ,
302- "resp_msg" , string (body ),
309+ "resp_msg" , string (respBody ),
303310 )
304311 lastErr = fmt .Errorf ("server error, attempt %d" , attempt )
305312 }
0 commit comments