d2lang/d2 · error

newDst not found

Error message

newDst not found

What it means

Same failure mode as newSrc but for the destination: ReconnectEdge parsed `dstKey` and could not find a matching object under the board's root. The rewire is aborted so the graph is never left with a dangling destination.

Source

Thrown at d2oracle/edit.go:205

	var dst *d2graph.Object
	if srcKey != nil {
		srcmk, err := d2parser.ParseMapKey(*srcKey)
		if err != nil {
			return nil, err
		}
		src, ok = boardG.Root.HasChild(d2graph.Key(srcmk.Key))
		if !ok {
			return nil, errors.New("newSrc not found")
		}
	}
	if dstKey != nil {
		dstmk, err := d2parser.ParseMapKey(*dstKey)
		if err != nil {
			return nil, err
		}
		dst, ok = boardG.Root.HasChild(d2graph.Key(dstmk.Key))
		if !ok {
			return nil, errors.New("newDst not found")
		}
	}

	refs := edge.References
	if baseAST != g.AST {
		refs = GetWriteableEdgeRefs(edge, baseAST)
		if len(refs) == 0 || refs[0].ScopeAST != baseAST {
			// TODO null
			return nil, OutsideScopeError{}
		}
	}
	ref := refs[0]

	// for loops where only one end is changing, node is always ensured
	if edge.Src != edge.Dst && (srcKey == nil || dstKey == nil) {
		var refEdges []*d2ast.Edge
		for _, ref := range refs {
			refEdges = append(refEdges, ref.Edge)

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Create the destination node first via d2oracle.Create, then call ReconnectEdge.
  2. Pre-validate dstKey with g.Root.HasChild on the parsed key.
  3. Pass the destination node's AbsID() as dstKey.
  4. Make sure boardPath matches the board where the dst node actually lives.

Example fix

// before
g, err = d2oracle.ReconnectEdge(g, []string{"layers.x"}, "a -> b", nil, &newDst) // newDst missing in board
// after
if _, ok := boardG.Root.HasChild([]string{"c"}); !ok {
    g, _, err = d2oracle.Create(g, nil, "c", nil)
}
g, err = d2oracle.ReconnectEdge(g, []string{"layers.x"}, "a -> b", nil, &newDst)
Defensive patterns

Strategy: validation

Validate before calling

dstmk, err := d2parser.ParseMapKey(newDst)
if err != nil { return err }
if _, ok := g.Root.HasChild(d2graph.Key(dstmk.Key)); !ok {
    g, _, err = d2oracle.Create(g, nil, newDst, nil)
    if err != nil { return err }
}

Type guard

func nodeExists(g *d2graph.Graph, id string) bool {
    mk, err := d2parser.ParseMapKey(id)
    if err != nil { return false }
    _, ok := g.Root.HasChild(d2graph.Key(mk.Key))
    return ok
}

Try / catch

g2, err := d2oracle.ReconnectEdge(g, boardPath, key, nil, &newDst)
if err != nil && err.Error() == "newDst not found" {
    g, _, err = d2oracle.Create(g, nil, newDst, nil)
    if err != nil { return err }
    g2, err = d2oracle.ReconnectEdge(g, boardPath, key, nil, &newDst)
}

Prevention

When it happens

Trigger: Calling d2oracle.ReconnectEdge with `dstKey` for a non-existent node: typo, dst node deleted, dst in another board than boardPath, or unquoted special characters in the ID path.

Common situations: Reconnecting an edge to a target the user just typed that was never created; tooling that assumes ReconnectEdge auto-creates endpoints (it does not for the dst lookup); nested board sketches where the target exists only on the root board.

Related errors


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