d2lang/d2 · error

edge not found

Error message

edge not found

What it means

ReconnectEdge is asked to reconnect the edge addressed by `key`, but the container object that the edge's key path resolves to does not exist in the board's graph. `boardG.Root.HasChild(d2graph.Key(mk.Key))` fails, meaning the prefix of the key before the edge has no matching object. The library refuses to proceed because there is no scope object to look the edge up under.

Source

Thrown at d2oracle/edit.go:161

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

	obj := boardG.Root
	if mk.Key != nil {
		var ok bool
		obj, ok = boardG.Root.HasChild(d2graph.Key(mk.Key))
		if !ok {
			return nil, errors.New("edge not found")
		}
	}

	edge, ok := obj.HasEdge(mk)
	if !ok {
		return nil, errors.New("edge not found")
	}

	if srcKey != nil {
		if edge.Src.AbsID() == *srcKey {
			srcKey = nil
		}
	}

	if dstKey != nil {
		if edge.Dst.AbsID() == *dstKey {
			dstKey = nil
		}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Verify the object prefix of the key exists in the graph before calling ReconnectEdge (compile the sketch and check HasChild / AbsID).
  2. Fix typos or quote identifiers containing special characters: use `x."a.b".(c -> d)` style quoting in the key string.
  3. Confirm you are targeting the right board via boardPath; the edge's container must exist in that nested board.
  4. Re-fetch the current key from the compiled graph instead of caching keys across edits.

Example fix

// before
g, err := d2oracle.ReconnectEdge(g, []string{"root"}, "contianer.(a -> b)", &newSrc, nil)
// after (typo fixed and existence pre-checked)
key := "container.(a -> b)"
if obj, ok := g.Root.HasChild([]string{"container"}); ok && obj.HasEdge(mk) {
    g, err = d2oracle.ReconnectEdge(g, []string{"root"}, key, &newSrc, nil)
}
Defensive patterns

Strategy: validation

Validate before calling

mk, err := d2parser.ParseMapKey(key)
if err != nil { return err }
if mk.Key != nil {
    if _, ok := g.Root.HasChild(d2graph.Key(mk.Key)); !ok {
        return fmt.Errorf("container %v does not exist", key)
    }
}

Type guard

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

Try / catch

g2, err := d2oracle.ReconnectEdge(g, boardPath, key, &src, &dst)
if err != nil {
    if err.Error() == "edge not found" {
        // re-derive key from current graph and retry once
    }
    return err
}

Prevention

When it happens

Trigger: Calling d2oracle.ReconnectEdge with a `key` like `x.(a -> b)` where the object path `x` (everything before the edge) does not exist in the graph, e.g. the node was deleted, renamed, or the key is typos/missing quotes for special characters.

Common situations: Editors replaying a stored key after a user renamed a container; keys built for the wrong board (nested boardPath given but edge lives on root); stale client state after a delete; keys whose IDs contain dots/spaces needing quoting (`"a.b"`) but passed raw.

Related errors


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