d2lang/d2 · error

decode icon URL for stable diagram hash: invalid offsets %d:

Error message

decode icon URL for stable diagram hash: invalid offsets %d:%d

What it means

After canonicalizing an icon URL, the walker records the value's byte span (valueStart from hashJSONValueStart to valueEnd from dec.InputOffset) so it can splice the stable form back into the output. If that span is inverted or extends past the input length, the offsets are inconsistent and this error is returned to avoid producing a corrupt hash output.

Source

Thrown at d2target/d2target.go:293

				valueStart, err := hashJSONValueStart(b, int(dec.InputOffset()))
				if err != nil {
					return err
				}
				var raw json.RawMessage
				if err := dec.Decode(&raw); err != nil {
					return fmt.Errorf("decode icon URL for stable diagram hash: %w", err)
				}
				if bytes.Equal(raw, []byte("null")) {
					continue
				}

				stable, err := stableHashURL(raw)
				if err != nil {
					return err
				}
				valueEnd := int(dec.InputOffset())
				if valueEnd < valueStart || valueEnd > len(b) {
					return fmt.Errorf("decode icon URL for stable diagram hash: invalid offsets %d:%d", valueStart, valueEnd)
				}
				out.Write(b[lastWrite:valueStart])
				out.Write(stable)
				lastWrite = valueEnd
			}
			closeToken, err := dec.Token()
			if err != nil {
				return err
			}
			if closeToken != json.Delim('}') {
				return fmt.Errorf("decode diagram hash JSON: got closing token %v, want }", closeToken)
			}
		case '[':
			for dec.More() {
				if err := walkValue(); err != nil {
					return err
				}
			}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Pass the exact, unmodified byte slice that was decoded — do not re-slice or mutate it mid-walk
  2. Do not feed already-stable-hashed output back through the canonicalizer
  3. Ensure single-threaded access to the buffer during hashing
  4. Re-generate the diagram JSON from the compiled diagram instead of reusing cached bytes

Example fix

// before
sub := b[10:] // offset mismatch
out, err := StableHashWithIcons(sub)
// after
out, err := StableHashWithIcons(b) // full buffer, no mutation
Defensive patterns

Strategy: validation

Validate before calling

if !json.Valid(b) {
	return errors.New("diagram JSON is malformed")
}

Prevention

When it happens

Trigger: Stable-hashing JSON where hashJSONValueStart's computed start offset and the decoder's end offset disagree — e.g. input mutated between calls, an offset from a different buffer, or non-standard whitespace/encoding confusing offset calculation.

Common situations: Custom callers passing a sub-slice of the buffer with an offset mismatch; concurrent modification of the byte slice during hashing; feeding pre-hashed (already canonicalized) JSON back through the function.

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/350f1bfadd0a56d2. Report an issue: GitHub.