Skip to content

Commit 885cd19

Browse files
Fix 412 race condition in tag delete command
Add retry logic (up to 3 attempts) for PreConditionFailedError in the delete_tags CLI command. The Zotero API uses optimistic concurrency control, and the library version can change between the version check and the DELETE request, causing spurious 412 errors. Also fix the temp_tag_in_library test fixture to re-fetch items before deletion to avoid stale version errors in cleanup. Fixes #35 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 620bf58 commit 885cd19

2 files changed

Lines changed: 23 additions & 8 deletions

File tree

pyzotero_cli/tag_cmds.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import time
12
import click
23
import json
34
from pyzotero import zotero
5+
from pyzotero import zotero_errors
46
from .utils import common_options, format_data_for_output, handle_zotero_exceptions_and_exit, create_click_exception, initialize_zotero_client
57

68
@click.group(name='tags')
@@ -67,8 +69,20 @@ def delete_tags(ctx, tag_names, force):
6769
click.echo("Operation cancelled.")
6870
return
6971

70-
try:
71-
result = zot.delete_tags(*tag_names)
72-
click.echo(f"Successfully deleted tags: {', '.join(tag_names)}")
73-
except Exception as e:
74-
handle_zotero_exceptions_and_exit(ctx, e)
72+
max_retries = 3
73+
for attempt in range(max_retries):
74+
try:
75+
result = zot.delete_tags(*tag_names)
76+
click.echo(f"Successfully deleted tags: {', '.join(tag_names)}")
77+
return
78+
except zotero_errors.PreConditionFailedError:
79+
if attempt < max_retries - 1:
80+
time.sleep(1)
81+
continue
82+
else:
83+
handle_zotero_exceptions_and_exit(ctx, zotero_errors.PreConditionFailedError(
84+
"Library version conflict persisted after retries. "
85+
"Another process may be modifying the library."
86+
))
87+
except Exception as e:
88+
handle_zotero_exceptions_and_exit(ctx, e)

tests/test_tag_cmds.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ def temp_tag_in_library(zot_instance):
2828

2929
yield tag_name # Yield the tag name to the test
3030

31-
# Cleanup
31+
# Cleanup - re-fetch item to get latest version before deleting
3232
try:
33-
if created_item and item_key:
34-
zot_api_client.delete_item(created_item) # Delete the temporary item
33+
if item_key:
34+
fresh_item = zot_api_client.item(item_key)
35+
zot_api_client.delete_item(fresh_item)
3536
except Exception as e:
3637
print(f"Error during tag fixture cleanup (deleting item {item_key}): {e}")
3738

0 commit comments

Comments
 (0)