d2lang/d2 · error
failed to unmarshal Range from %q: missing End field
Error message
failed to unmarshal Range from %q: missing End field
What it means
Range.UnmarshalText parses a Range string of the form "path,start,end" where start and end are positions separated by the last '-' (e.g. "file.d2,1:0-1:5"). This error is wrapped with 'failed to unmarshal Range from %q' when no '-' exists, meaning the End position field is missing. It is thrown by MakeRange/deserialization when given a malformed Range text.
Source
Thrown at d2ast/d2ast.go:136
// OneLine returns true if the Range starts and ends on the same line.
func (r Range) OneLine() bool {
return r.Start.Line == r.End.Line
}
// See docs on Range.
func (r Range) MarshalText() ([]byte, error) {
start, _ := r.Start.MarshalText()
end, _ := r.End.MarshalText()
return []byte(fmt.Sprintf("%s,%s-%s", r.Path, start, end)), nil
}
// See docs on Range.
func (r *Range) UnmarshalText(b []byte) (err error) {
defer xdefer.Errorf(&err, "failed to unmarshal Range from %q", b)
i := bytes.LastIndexByte(b, '-')
if i == -1 {
return errors.New("missing End field")
}
end := b[i+1:]
b = b[:i]
i = bytes.LastIndexByte(b, ',')
if i == -1 {
return errors.New("missing Start field")
}
start := b[i+1:]
b = b[:i]
r.Path = string(b)
err = r.Start.UnmarshalText(start)
if err != nil {
return err
}
return r.End.UnmarshalText(end)
}View on GitHub (pinned to 0d69dca6f5)
Solutions
- Include both positions separated by '-' in the format "path,start-end" (each position is "line:col:byte"), e.g. "file.d2,1:0:0-1:5:5".
- Check the input string for accidental truncation or missing '-' before passing it to UnmarshalText.
- Verify the string was produced by Range.MarshalText rather than constructed manually.
Example fix
// before
r.UnmarshalText("file.d2,1:0")
// after
r.UnmarshalText("file.d2,1:0:0-1:5:5") Defensive patterns
Strategy: validation
Validate before calling
func validRangeText(s string) bool { return strings.Contains(s, "-") && strings.Contains(s, ",") }
if !validRangeText(input) { return fmt.Errorf("range must be \"path,start-end\", got %q", input) } Type guard
func isRangeText(s string) bool { i := strings.LastIndexByte(s, '-'); return i > 0 && strings.LastIndexByte(s[:i], ',') != -1 } Try / catch
var r d2ast.Range
if err := r.UnmarshalText(input); err != nil {
return fmt.Errorf("invalid range %q: %w", input, err)
} Prevention
- Always produce range strings via MarshalText, never by hand-concatenation.
- Sanity-check for '-' and ',' separators before deserializing.
- Log the full raw input when deserialization fails to spot truncation.
When it happens
Trigger: Calling Range.UnmarshalText or MakeRange with a string containing no '-' separator between the start and end positions, e.g. "file.d2,1:0".
Common situations: Hand-editing serialized range strings, truncating a range when copying from logs, or passing a Position string where a full Range string was expected.
Related errors
- failed to unmarshal Range from %q: missing Start field
- failed to unmarshal Position from %q: expected three fields
- invalid gradient syntax
- no parameters in gradient
- no color stops in gradient
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/4b5bc0084645425b.
Report an issue: GitHub.