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
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.
Summary
d2fetches a remoteicon:URL withAccept-Encoding: br, receives a brotli-compressed body, and embeds the compressed bytes into thedata: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.comitself, which is what the docs recommend.Version
Installed via Homebrew (
d2 0.8.1), macOS arm64.Minimal reproduction
Then extract the embedded payload:
Evidence that it is the compressed body
The embedded payload is byte-identical to the brotli response, and decompresses to the real SVG:
05 2d 02 00curl -H 'Accept-Encoding: br' …05 2d 02 00Without compression the server returns a normal 1115-byte SVG, so the icon itself is fine:
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-Encodingbefore embedding, or do not offer compression on icon fetches.Impact
The failure is silent and sticky:
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:
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.