d2lang/d2 · error
decode icon URL for stable diagram hash: missing field %q
Error message
decode icon URL for stable diagram hash: missing field %q
What it means
When re-emitting the icon URL fields in canonical (legacy) order for hashing, stableHashURL requires every field in legacyURLFieldOrder to be present. Because Go maps lose ordering, the count check (error 192) may pass with the same number of fields only when names match exactly; if a name differs, the lookup by canonical name fails and reports the missing field.
Source
Thrown at d2target/d2target.go:363
}
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 {
return nil, fmt.Errorf("decode icon URL for stable diagram hash: missing field %q", name)
}
if i > 0 {
out.WriteByte(',')
}
out.WriteByte('"')
out.WriteString(name)
out.WriteString(`":`)
out.Write(value)
}
out.WriteByte('}')
return out.Bytes(), nil
}
func (diagram Diagram) HasShape(condition func(Shape) bool) bool {
for _, d := range diagram.Layers {
if d.HasShape(condition) {
return true
}View on GitHub (pinned to 0d69dca6f5)
Solutions
- Make the JSON keys exactly match legacyURLFieldOrder names and order of appearance in that array
- Fix key casing to match the expected field name reported in %q
- Update legacyURLFieldOrder if a field was intentionally renamed
- Regenerate the payload using d2's own serialization
Example fix
// before
{"Url":"https://x/i.svg"}
// after
{"url":"https://x/i.svg"} Defensive patterns
Strategy: validation
Validate before calling
var fields map[string]json.RawMessage
if err := json.Unmarshal(raw, &fields); err != nil {
return err
}
for _, name := range legacyURLFieldOrder {
if _, ok := fields[name]; !ok {
return fmt.Errorf("icon URL JSON missing field %q", name)
}
} Type guard
func hasAllLegacyFields(raw json.RawMessage) bool {
var m map[string]json.RawMessage
if json.Unmarshal(raw, &m) != nil {
return false
}
for _, name := range legacyURLFieldOrder {
if _, ok := m[name]; !ok {
return false
}
}
return true
} Try / catch
hashed, err := stableHashURL(raw)
if err != nil {
log.Printf("icon URL field mismatch: %v", err)
return err
} Prevention
- Match key names/casing exactly to the struct json tags
- Regenerate payloads after any struct rename
- Never rename exported struct fields without updating legacyURLFieldOrder
When it happens
Trigger: An icon URL JSON object whose keys don't match the exact names in legacyURLFieldOrder (renamed key, typo, different casing) — count matches but a canonical name lookup misses.
Common situations: Renaming struct fields in the URL type, casing mismatches from other serializers (e.g. camelCase vs snake_case), or hand-written JSON with a typo.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- decode icon URL for stable diagram hash: got %d fields, want
- decode icon URL for stable diagram hash: missing value
- 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/0038a1687342277b.
Report an issue: GitHub.