666ghj/MiroFish · error · StarHistoryError
generated SVG must contain both reviewed images
Error message
generated SVG must contain both reviewed images
What it means
Raised by _validate_svg after iterating the whole document: the exact-match counters for the reviewed avatar <image> and watermark <image> must each equal 1. Any deviation — zero occurrences (template dropped one), duplicates, or images whose attribute dicts no longer byte-match the pinned sets at scripts/star_history.py:1199-1213 — fails here or earlier with 'unreviewed image'.
Source
Thrown at scripts/star_history.py:1249
exact_values = exact_attribute_values.get(raw_name)
if exact_values is not None and value not in exact_values:
raise StarHistoryError("generated SVG contains an unsafe attribute value")
if raw_name == "href":
if local_name != "image" or value not in {
OWNER_AVATAR_DATA_URI,
WATERMARK_LOGO_DATA_URI,
}:
raise StarHistoryError("generated SVG contains an external resource")
lowered = value.lower().replace(" ", "")
if "url(" in lowered and lowered not in {
"url(#xkcdify)",
"url(#clip-circle-title)",
}:
raise StarHistoryError("generated SVG contains an external resource")
if lowered.startswith(("http:", "https:", "//")):
raise StarHistoryError("generated SVG contains an external resource")
if avatar_count != 1 or watermark_count != 1:
raise StarHistoryError("generated SVG must contain both reviewed images")
try:
avatar = base64.b64decode(OWNER_AVATAR_BASE64, validate=True)
except ValueError as exc:
raise StarHistoryError("reviewed avatar data is invalid") from exc
if (
len(avatar) > MAX_INLINE_AVATAR_BYTES
or hashlib.sha256(avatar).hexdigest() != OWNER_AVATAR_SHA256
or _reviewed_avatar_dimensions(avatar) != OWNER_AVATAR_DIMENSIONS
):
raise StarHistoryError("reviewed avatar data is invalid")
try:
watermark = base64.b64decode(WATERMARK_LOGO_BASE64, validate=True)
except ValueError as exc:
raise StarHistoryError("reviewed watermark data is invalid") from exc
png_header = (
watermark.startswith(b"\x89PNG\r\n\x1a\n")
and watermark[8:12] == (13).to_bytes(4, "big")
and watermark[12:16] == b"IHDR"View on GitHub (pinned to b5b53acc57)
Solutions
- Ensure every rendered SVG variant contains exactly one <image> matching avatar_attributes and one matching watermark_attributes (scripts/star_history.py:1199-1213), attribute-for-attribute.
- If you intentionally moved the avatar/watermark (new coordinates), update both the pinned attribute dicts in the validator and the template in the same change.
- If you intentionally removed the watermark, that violates the shipped design contract — restore it rather than patching the check.
- Run _validate_svg over both light and dark outputs after any template edit (they are validated in _write_outputs at lines 1326-1327).
Example fix
# before: watermark image deleted from the dark template
# (watermark_count == 0 at the end of _validate_svg)
# after: keep exactly one pinned watermark <image> per SVG
<image x="635" y="508.333" width="20" height="20"
href="{WATERMARK_LOGO_DATA_URI}"/> Defensive patterns
Strategy: validation
Validate before calling
def has_both_reviewed_images(root) -> bool:
avatar = watermark = 0
for el in root.iter():
if el.tag.endswith("}image"):
attrs = dict(el.attrib)
if attrs == AVATAR_ATTRIBUTES: # pinned dicts from the validator
avatar += 1
elif attrs == WATERMARK_ATTRIBUTES:
watermark += 1
return avatar == 1 and watermark == 1 Try / catch
try:
_validate_svg(svg_bytes)
except StarHistoryError as exc:
if "reviewed images" in str(exc):
# count <image> elements; restore or re-pin the avatar/watermark blocks
... Prevention
- Every rendered variant must include exactly one pinned avatar and one pinned watermark <image>.
- When repositioning the images, update the pinned attribute dicts and the template together.
- Add a render test per variant asserting _validate_svg passes.
When it happens
Trigger: Deleting the watermark image from the template; moving/duplicating the avatar; changing any of its pinned attributes (x/y/width/height/clip-path) so element.attrib != avatar_attributes/watermark_attributes exactly (those cases raise 'unreviewed image' at line 1219, then this fires if the count is 0); rendering a variant (e.g. a banner without watermark).
Common situations: Someone removes the 'star-history.com' watermark for aesthetics; the avatar image attributes are reordered or an attribute added (attrib dict comparison fails); a new chart variant is added to _output_payloads that was never wired to include both images.
Related errors
- generated SVG must be strict UTF-8
- generated SVG contains an unsafe attribute value
- generated SVG contains an external resource
- reviewed watermark data is invalid
- t('step5.selectAgentFirst')
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/b7572c3811551bb8.
Report an issue: GitHub.