d2lang/d2 · error

edgeKey must refer to an existing edge

Error message

edgeKey must refer to an existing edge

What it means

ReconnectEdge requires the edgeKey to include an explicit edge index (e.g. `(a -> b)[0]`) so it knows which edge to modify when multiple edges exist on the key. If mk.EdgeIndex is nil after parsing, the key doesn't disambiguate an existing edge and this error is thrown.

Source

Thrown at d2oracle/edit.go:135

			return nil, fmt.Errorf("board %v AST not found", boardPath)
		}
	}

	return recompile(g)
}

func ReconnectEdge(g *d2graph.Graph, boardPath []string, edgeKey string, srcKey, dstKey *string) (_ *d2graph.Graph, err error) {
	mk, err := d2parser.ParseMapKey(edgeKey)
	if err != nil {
		return nil, err
	}

	if len(mk.Edges) == 0 {
		return nil, errors.New("edgeKey must be an edge")
	}

	if mk.EdgeIndex == nil {
		return nil, errors.New("edgeKey must refer to an existing edge")
	}

	edgeTrimCommon(mk)

	boardG := g
	baseAST := g.AST

	if len(boardPath) > 0 {
		// When compiling a nested board, we can read from boardG but only write to baseBoardG
		boardG = GetBoardGraph(g, boardPath)
		if boardG == nil {
			return nil, fmt.Errorf("board %v not found", boardPath)
		}
		// TODO beter name
		baseAST = boardG.BaseAST
		if baseAST == nil {
			return nil, fmt.Errorf("board %v cannot be modified through this file", boardPath)
		}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Use the indexed key form: "(a -> b)[0]" instead of "a -> b".
  2. Take the exact key from g.Edges[i].AST / the board AST rather than constructing it manually.
  3. If multiple edges share the key, identify the correct index and pass it via edgeIndex while the key includes the (…)[] form.
  4. Use d2oracle to inspect g.Edges and reuse its key string verbatim.

Example fix

// before
ReconnectEdge(g, "a -> b", 0, "c", "d")
// after
ReconnectEdge(g, "(a -> b)[0]", 0, "c", "d")
Defensive patterns

Strategy: validation

Validate before calling

mk, _ := d2parser.ParseMapKey(edgeKey)
if mk != nil && mk.EdgeIndex == nil {
  edgeKey = fmt.Sprintf("(%s)[0]", edgeKey)
}

Type guard

func edgeKeyHasIndex(edgeKey string) bool {
  mk, err := d2parser.ParseMapKey(edgeKey)
  return err == nil && mk != nil && mk.EdgeIndex != nil
}

Try / catch

newKey, err := d2oracle.ReconnectEdge(g, edgeKey, edgeIndex, newSrc, newDst)
if err != nil && err.Error() == "edgeKey must refer to an existing edge" {
  // add the (key)[index] form and retry
}

Prevention

When it happens

Trigger: Calling ReconnectEdge with an edge key like "a -> b" that omits the (key)[index] form, or with an index/key combination that parsed to nil EdgeIndex — commonly when the key has multiple edge representations.

Common situations: Using g.Edges[i].Key straight from the graph when it lacks an index for multi-edge keys; hand-writing edge keys in tools and omitting the [0] index; multiple connections between the same endpoints requiring index disambiguation.

Related errors


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