d2lang/d2 · error

"%s" is invalid

Error message

"%s" is invalid

What it means

GetRefRanges parses the given key string into a MapKey AST node; if the parse succeeds but yields neither a Key nor Edges, the string is not a valid D2 key expression, so it cannot have reference ranges and this error is returned.

Source

Thrown at d2lsp/d2lsp.go:25

	"github.com/d2lang/d2/d2ast"
	"github.com/d2lang/d2/d2ir"
	"github.com/d2lang/d2/d2parser"
	"github.com/d2lang/d2/lib/memfs"
)

func GetRefRanges(path string, fs map[string]string, boardPath []string, key string) (ranges []d2ast.Range, importRanges []d2ast.Range, _ error) {
	m, err := getBoardMap(path, fs, boardPath)
	if err != nil {
		return nil, nil, err
	}

	mk, err := d2parser.ParseMapKey(key)
	if err != nil {
		return nil, nil, err
	}
	if mk.Key == nil && len(mk.Edges) == 0 {
		return nil, nil, fmt.Errorf(`"%s" is invalid`, key)
	}

	var f *d2ir.Field
	if mk.Key != nil {
		for _, p := range mk.Key.Path {
			f = m.GetField(p.Unbox())
			if f == nil {
				return nil, nil, nil
			}
			m = f.Map()
		}
	}

	if len(mk.Edges) > 0 {
		eids := d2ir.NewEdgeIDs(mk)
		var edges []*d2ir.Edge
		for _, eid := range eids {
			edges = append(edges, m.GetEdges(eid, nil, nil)...)

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Trim and validate the key string is a non-empty valid D2 key (e.g. "a.b" or "a -> b") before calling GetRefRanges
  2. Check that the text extraction (position slicing) around the cursor captures the key, not just whitespace
  3. Parse the document with d2parser first and pass a key from a real MapKey node

Example fix

// before
ranges, _, err := d2lsp.GetRefRanges("   ", fs, boardPath, m)
// after
key := strings.TrimSpace(selection)
if key == "" { return nil, nil, errors.New("empty key") }
ranges, _, err := d2lsp.GetRefRanges(key, fs, boardPath, m)
Defensive patterns

Strategy: validation

Validate before calling

key = strings.TrimSpace(key)
if key == "" { return errors.New("key must be a non-empty D2 key") }
if mk, err := d2parser.ParseMapKey(key); err != nil || (mk.Key == nil && len(mk.Edges) == 0) {
	return fmt.Errorf("invalid key %q", key)
}

Type guard

func isValidD2Key(key string) bool {
	mk, err := d2parser.ParseMapKey(key)
	return err == nil && (mk.Key != nil || len(mk.Edges) > 0)
}

Try / catch

ranges, _, err := d2lsp.GetRefRanges(key, fs, boardPath, m)
if err != nil && strings.Contains(err.Error(), "is invalid") {
	return fmt.Errorf("skipping non-key selection %q", key)
}

Prevention

When it happens

Trigger: Calling d2lsp.GetRefRanges(key, ...) with an empty string, whitespace, or a token sequence that parses to an empty MapKey (no key path and no edge).

Common situations: LSP/tooling callers passing raw user selections that include only comments or operators; slicing document text incorrectly so the key text is empty; feeding malformed fragments from external tools.

Related errors


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