d2lang/d2 · error

failed to unmarshal Range from %q: missing Start field

Error message

failed to unmarshal Range from %q: missing Start field

What it means

After extracting the End field via the last '-', UnmarshalText looks for a ',' separating the path from the Start position. This error means no ',' was found, so the Start field is missing. The wrapping message 'failed to unmarshal Range from %q' includes the offending input.

Source

Thrown at d2ast/d2ast.go:143

	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)
}

func (r Range) Before(r2 Range) bool {
	return r.Start.Before(r2.Start)
}

// Position represents a line:column and byte position in a file.
//

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Format the input as "path,start-end", e.g. "file.d2,1:0:0-1:5:5".
  2. Ensure the path segment followed by ',' is present before the start position.
  3. Round-trip through MarshalText to get a canonical string instead of hand-building one.

Example fix

// before
r.UnmarshalText("1:0:0-1:5:5")
// after
r.UnmarshalText("file.d2,1:0:0-1:5:5")
Defensive patterns

Strategy: validation

Validate before calling

func hasStartField(s string) bool { i := strings.LastIndexByte(s, '-'); return i > 0 && strings.Contains(s[:i], ",") }
if !hasStartField(input) { return errors.New("range text needs a comma before the start position") }

Type guard

func isFullRangeText(s string) bool { parts := strings.SplitN(s, "-", 2); return len(parts) == 2 && strings.Contains(parts[0], ",") }

Try / catch

var r d2ast.Range
if err := r.UnmarshalText(input); err != nil {
    return fmt.Errorf("range %q missing start/path: %w", input, err)
}

Prevention

When it happens

Trigger: Calling Range.UnmarshalText/MakeRange with a string that has a '-' but no ',' (e.g. "1:0-1:5" without the path part, or "file.d2 1:0-1:5").

Common situations: Passing a bare position-pair string instead of the full "path,start-end" format, or dropping the path and its comma when transforming serialized values.

Related errors


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