d2lang/d2 · error
decode icon URL for stable diagram hash: missing value
Error message
decode icon URL for stable diagram hash: missing value
What it means
This error comes from the internal parser that scans the raw JSON of an icon URL shape while building a stable diagram hash (hash must be identical across serialization variants). It skips whitespace after a key and, if the JSON ends before any value appears, it reports that the value is missing. It guards the hashing pipeline from truncated or hand-mangled raw JSON.
Source
Thrown at d2target/d2target.go:344
return nil, err
}
out.Write(b[lastWrite:])
return out.Bytes(), nil
}
func hashJSONValueStart(b []byte, keyEnd int) (int, error) {
i := keyEnd
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\r' || b[i] == '\n') {
i++
}
if i < len(b) && b[i] == ':' {
i++
}
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\r' || b[i] == '\n') {
i++
}
if i >= len(b) {
return 0, fmt.Errorf("decode icon URL for stable diagram hash: missing value")
}
return i, nil
}
func stableHashURL(raw json.RawMessage) ([]byte, error) {
fields := make(map[string]json.RawMessage, len(legacyURLFieldOrder))
if err := json.Unmarshal(raw, &fields); err != nil {
return nil, fmt.Errorf("decode icon URL for stable diagram hash: %w", err)
}
if len(fields) != len(legacyURLFieldOrder) {
return nil, fmt.Errorf("decode icon URL for stable diagram hash: got %d fields, want %d", len(fields), len(legacyURLFieldOrder))
}
var out bytes.Buffer
out.WriteByte('{')
for i, name := range legacyURLFieldOrder {
value, ok := fields[name]
if !ok {View on GitHub (pinned to 0d69dca6f5)
Solutions
- Inspect the raw icon URL JSON passed into hashing and make sure each key is followed by a complete value
- Regenerate the diagram JSON from the library instead of editing it by hand
- Validate the JSON parses with encoding/json before feeding it to stable hashing
- Check for code that trims or truncates serialized payloads before hashing
Example fix
// before
raw := []byte(`{"val":"`)
// after
raw := []byte(`{"val":"https://example.com/icon.svg"}`) Defensive patterns
Strategy: validation
Validate before calling
raw := iconURLRawJSON
if !json.Valid(raw) {
return fmt.Errorf("icon URL raw JSON is not valid JSON: %s", raw)
}
var probe map[string]json.RawMessage
if err := json.Unmarshal(raw, &probe); err != nil {
return err
}
if len(probe) != len(legacyURLFieldOrder) {
return fmt.Errorf("icon URL JSON has %d fields, want %d", len(probe), len(legacyURLFieldOrder))
} Type guard
func isValidIconURLJSON(raw json.RawMessage) bool {
if !json.Valid(raw) {
return false
}
var probe map[string]json.RawMessage
return json.Unmarshal(raw, &probe) == nil && len(probe) == len(legacyURLFieldOrder)
} Try / catch
hashed, err := stableHashURL(raw)
if err != nil {
log.Printf("icon URL hash skipped, malformed JSON %q: %v", raw, err)
return err
} Prevention
- Always produce icon URL JSON via d2's own marshaling, never by hand
- Run json.Valid on any raw JSON before hashing
- Never truncate serialized payloads before passing them downstream
When it happens
Trigger: Calling the stable-hash code path with a raw JSON icon URL payload that ends right after a key/colon, i.e. truncated JSON with no value token before EOF.
Common situations: Manually constructed or partially written JSON for icon nearShape/shape URLs, external tools truncating output, or version drift where a serializer emits incomplete objects into the hash input.
Related errors
- decode icon URL for stable diagram hash: got %d fields, want
- decode icon URL for stable diagram hash: missing field %q
- decode diagram hash JSON: object key has type %T
- decode icon URL for stable diagram hash: %w
- decode icon URL for stable diagram hash: invalid offsets %d:
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/0f52d4af85b90931.
Report an issue: GitHub.