@@ -1356,17 +1356,38 @@ def get_diff_scan_artifacts(
13561356 "before" : head_full_scan_id ,
13571357 "after" : new_full_scan_id ,
13581358 "description" : f"Socket Security CLI v{ __version__ } scan comparison" ,
1359- # A rerun against the same pair of scans returns the existing diff
1360- # scan instead of failing with a 409.
1361- "on_duplicate" : "redirect" ,
13621359 }
1363- result = self .sdk .diffscans .create_from_ids (self .config .org_slug , create_params )
1364- diff_scan = result .get ("diff_scan" ) or {}
1360+ try :
1361+ result = self .sdk .diffscans .create_from_ids (self .config .org_slug , create_params )
1362+ diff_scan = result .get ("diff_scan" ) or {}
1363+ response_summary = result
1364+ except APIFailure as error :
1365+ if error .status_code != 409 :
1366+ raise
1367+
1368+ # Do not use on_duplicate=redirect here. The SDK follows that 302
1369+ # automatically with a GET that lacks cached=true, which can leave
1370+ # the connection idle while an existing diff scan is still computing.
1371+ # Resolve the duplicate resource explicitly so every result fetch
1372+ # continues through the bounded cached polling path below.
1373+ existing = self .sdk .diffscans .list (
1374+ self .config .org_slug ,
1375+ params = {
1376+ "before_full_scan_id" : head_full_scan_id ,
1377+ "after_full_scan_id" : new_full_scan_id ,
1378+ "per_page" : 1 ,
1379+ },
1380+ )
1381+ matches = existing .get ("results" ) or []
1382+ diff_scan = matches [0 ] if matches else {}
1383+ response_summary = existing
1384+
13651385 diff_scan_id = diff_scan .get ("id" )
13661386 if not diff_scan_id :
1367- raise Exception (f"Error creating diff scan: unexpected response: { str (result )[:500 ]} " )
1368- # An on_duplicate redirect can land on an already-computed diff scan, in
1369- # which case the create response already carries the artifacts.
1387+ raise Exception (
1388+ "Error creating or resolving diff scan: "
1389+ f"unexpected response: { str (response_summary )[:500 ]} "
1390+ )
13701391 artifacts_dict = diff_scan .get ("artifacts" )
13711392
13721393 # cached=true is the polling contract (202 while computing, 200 when
0 commit comments