ArchiveBox/ArchiveBox · error · ValueError
Tag "{existing.name}" already exists
Error message
Tag "{existing.name}" already exists What it means
rename_tag raises ValueError when another tag (different primary key) already exists with a case-insensitively equal name after normalization. This guards Tag.name uniqueness when renaming, since Django's unique constraint alone can miss case-variant duplicates. It fires before any rename is applied, so state is unchanged.
Source
Thrown at archivebox/core/tag_util.py:173
existing = Tag.objects.filter(name__iexact=normalized_name).first()
if existing:
return existing, False
tag = Tag.objects.create(
name=normalized_name,
created_by=created_by,
)
return tag, True
def rename_tag(tag: Tag, name: str) -> Tag:
normalized_name = normalize_tag_name(name)
if not normalized_name:
raise ValueError("Tag name is required")
existing = Tag.objects.filter(name__iexact=normalized_name).exclude(pk=tag.pk).first()
if existing:
raise ValueError(f'Tag "{existing.name}" already exists')
if tag.name != normalized_name:
tag.name = normalized_name
tag.save()
return tag
def delete_tag(tag: Tag) -> tuple[int, dict[str, int]]:
return tag.delete()
def export_tag_urls(tag: Tag) -> str:
urls = tag.snapshot_set.order_by("-downloaded_at", "-created_at", "-pk").values_list("url", flat=True)
return "\n".join(urls)
def export_tag_snapshots_jsonl(tag: Tag) -> str:
snapshots = tag.snapshot_set.order_by("-downloaded_at", "-created_at", "-pk").prefetch_related("tags")View on GitHub (pinned to 74564b2822)
Solutions
- Choose a different target name that does not collide (case-insensitively) with any existing tag.
- Merge the two tags first (move all Snapshot-tag links onto the existing tag, delete the duplicate), then rename if still needed.
- Query Tag.objects.filter(name__iexact=normalize_tag_name(new_name)).exclude(pk=tag.pk) before calling rename_tag to pre-check collisions.
Example fix
// before
rename_tag(tag, "News") # ValueError: Tag "news" already exists
// after
existing = Tag.objects.get(name__iexact="news")
for snap in tag.snapshot_set.all():
existing.snapshot_set.add(snap)
tag.snapshot_set.clear()
tag.delete() Defensive patterns
Strategy: validation
Validate before calling
from archivebox.core.tag_util import normalize_tag_name
new_name = normalize_tag_name(candidate)
collision = Tag.objects.filter(name__iexact=new_name).exclude(pk=tag.pk).exists()
if not new_name or collision:
# pick another name or merge instead of renaming
... Try / catch
try:
rename_tag(tag, new_name)
except ValueError as e:
if "already exists" in str(e):
merge_into_existing(tag, Tag.objects.get(name__iexact=normalize_tag_name(new_name)))
else:
raise Prevention
- Always normalize the name with normalize_tag_name before suggesting a rename.
- Pre-check case-insensitive collisions with Tag.objects.filter(name__iexact=...).
- Prefer merging duplicate tags over creating case-variant names.
When it happens
Trigger: Calling rename_tag(tag, name) where normalize_tag_name(name) matches (case-insensitively) the name of a different Tag row, e.g. renaming 'News' to 'news' when a tag 'news' already exists.
Common situations: Users rename a tag to a capitalized/lowercased variant of an existing tag; case-insensitive DB collations or prior imports created near-duplicate tags; admin/API flows allow overlapping names.
Related errors
- Either tag_name or tag_id is required
- Tag name is required
- message
- Valid credentials but User does not have permission (make su
- str(err)
AI-assisted analysis of ArchiveBox/ArchiveBox@74564b2822 (2026-08-28).
Data as JSON: /api/errors/9e6482986fce6705.
Report an issue: GitHub.