Skip to content

Remote icons embed the compressed HTTP body verbatim — every icon renders broken, with exit code 0 #2849

Description

@larryoverbeck

Summary

d2 fetches a remote icon: URL with Accept-Encoding: br, receives a brotli-compressed body, and embeds the compressed bytes into the data:image/svg+xml;base64,… payload without decompressing. Every remote icon in the output is a broken image. Exit code is 0 and nothing is logged.

Notably this affects icons.terrastruct.com itself, which is what the docs recommend.

Version

$ d2 --version
v0.8.1

Installed via Homebrew (d2 0.8.1), macOS arm64.

Minimal reproduction

// repro.d2
a: "remote icon" { icon: https://icons.terrastruct.com/aws%2FCompute%2FAWS-Lambda.svg }
$ d2 repro.d2 out.svg
success: successfully compiled repro.d2 to out.svg in 89ms

Then extract the embedded payload:

import base64, re
s = open('out.svg').read()
raw = base64.b64decode(re.search(r'href="data:image/svg\+xml;base64,([A-Za-z0-9+/=]+)"', s).group(1))
print(len(raw), raw[:4].hex(' '))
# 557  05 2d 02 00

Evidence that it is the compressed body

The embedded payload is byte-identical to the brotli response, and decompresses to the real SVG:

bytes first 4
payload d2 embedded 557 05 2d 02 00
curl -H 'Accept-Encoding: br' … 557 05 2d 02 00
$ curl -s -H "Accept-Encoding: br" -D - -o br.bin \
    "https://icons.terrastruct.com/aws%2FCompute%2FAWS-Lambda.svg" | grep -i content-encoding
content-encoding: br

$ brotli -d -c payload.bin | head -c 24
<svg xmlns="http://www.w

Without compression the server returns a normal 1115-byte SVG, so the icon itself is fine:

$ curl -s "https://icons.terrastruct.com/aws%2FCompute%2FAWS-Lambda.svg" | head -c 20
<svg xmlns="http://w

I have also seen a gzip-shaped payload (1f 8b 08 00) from the same host on a different run, so this is not specific to brotli — it is whatever gets negotiated.

Expected

Either decompress per Content-Encoding before embedding, or do not offer compression on icon fetches.

Impact

The failure is silent and sticky:

  • exit code 0, no warning — CI commits broken diagrams
  • it degrades existing diagrams: re-rendering an unchanged source replaces good icons with broken ones
  • a freshness/staleness check that compares source hash to rendered output cannot see it, because both sides move together

In our repo this hit 8 committed diagrams. It went unnoticed for three weeks because nobody re-rendered an icon-using diagram in that window; a human eventually spotted that the pictures looked wrong.

Workaround

Vendoring the icon locally is unaffected, since nothing is fetched:

a: "remote icon" { icon: https://icons.terrastruct.com/aws%2FCompute%2FAWS-Lambda.svg }  // 739 bytes, not an SVG
b: "local icon"  { icon: ./aws-lambda.svg }                                              // 1556 bytes, valid SVG

Possibly related

#2367 (closed by #2370) reported broken icons with exit code 0, but for hosts other than icons.terrastruct.com, and #2370 sniffs the MIME type from the file signature. That cannot help here: a compressed body has no SVG signature, so it is embedded as-is. The suggestion in that thread — that d2 should validate the icon it pulled rather than emit a broken diagram successfully — would have caught this case too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions