d2lang/d2 · error
decode icon URL for stable diagram hash: got %d fields, want
Error message
decode icon URL for stable diagram hash: got %d fields, want %d
What it means
After unmarshaling, stableHashURL verifies the icon URL object contains exactly the fields in legacyURLFieldOrder so the hash stays stable across struct versions. This error fires when the field count differs, indicating the raw JSON has extra, missing, or renamed fields relative to the expected legacy layout.
Source
Thrown at d2target/d2target.go:355
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 {
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('}')View on GitHub (pinned to 0d69dca6f5)
Solutions
- Compare the raw object's keys against legacyURLFieldOrder (d2target/d2target.go:227) and remove unexpected fields or supply the missing ones
- If you changed the URL struct, update legacyURLFieldOrder to match
- Invalidate/rebuild cached persisted diagrams produced by older versions
- Pin consistent d2 versions for serialization and hashing
Example fix
// before (extra field)
{"url":"https://x/i.svg","height":24}
// 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
}
if len(fields) != len(legacyURLFieldOrder) {
return fmt.Errorf("field count %d != expected %d", len(fields), len(legacyURLFieldOrder))
} Type guard
func hasExpectedFieldCount(raw json.RawMessage) bool {
var m map[string]json.RawMessage
if json.Unmarshal(raw, &m) != nil {
return false
}
return len(m) == len(legacyURLFieldOrder)
} Try / catch
hashed, err := stableHashURL(raw)
if err != nil {
if strings.Contains(err.Error(), "fields, want") {
log.Printf("schema drift in icon URL JSON: %v", err)
}
return err
} Prevention
- Update legacyURLFieldOrder whenever the URL struct changes
- Invalidate hashes/caches when the schema version changes
- Keep serialization and hashing in the same module version
When it happens
Trigger: Passing an icon URL JSON object with a different number of keys than len(legacyURLFieldOrder) — extra fields, missing fields, or a struct version mismatch between serializer and hashing code.
Common situations: Mixing versions of d2target-generated data (old caches, persisted diagrams hashed with newer code), or adding a field to the URL struct without updating legacyURLFieldOrder.
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: missing field %q
- 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/e4eb626007ddb005.
Report an issue: GitHub.