d2lang/d2 · error

can only set one edge at a time

Error message

can only set one edge at a time

What it means

d2oracle's Set/Create path (`_set`) supports addressing at most one edge per key. The parsed key contained a multi-edge chain (e.g. `(a -> b -> c)` produces two edges), which is ambiguous for setting a single value, so it is rejected.

Source

Thrown at d2oracle/edit.go:340

	}
	return g2, nil
}

// TODO merge flat styles
func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) error {
	if tag != nil {
		if hasSpace(*tag) {
			return fmt.Errorf("spaces are not allowed in blockstring tags")
		}
	}

	mk, err := d2parser.ParseMapKey(key)
	if err != nil {
		return err
	}

	if len(mk.Edges) > 1 {
		return errors.New("can only set one edge at a time")
	}

	if value != nil {
		mk.Value = d2ast.MakeValueBox(d2ast.RawString(*value, false))
	} else {
		mk.Value = d2ast.ValueBox{}
	}
	if tag != nil && value != nil {
		mk.Value = d2ast.MakeValueBox(&d2ast.BlockString{
			Tag:   *tag,
			Value: *value,
		})
	}

	scope := baseAST
	edgeTrimCommon(mk)
	obj := g.Root
	toSkip := 1

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Split the chain and address a single edge with an index: `(a -> b -> c)[0].style.opacity` instead of `(a -> b -> c).style.opacity`.
  2. Perform one Set call per edge segment.
  3. If the goal is to set an attribute of an object rather than an edge, drop the edge portion from the key.
  4. Validate len(mk.Edges) <= 1 client-side before calling Set/Create.

Example fix

// before
g, err = d2oracle.Set(g, nil, "(a -> b -> c).style.opacity", &val)
// after
g, err = d2oracle.Set(g, nil, "(a -> b -> c)[0].style.opacity", &val)
Defensive patterns

Strategy: validation

Validate before calling

mk, err := d2parser.ParseMapKey(key)
if err != nil { return err }
if len(mk.Edges) > 1 {
    return fmt.Errorf("key %q contains %d edges; split and address one with an [index]", key, len(mk.Edges))
}

Type guard

func isSingleEdgeKey(key string) bool {
    mk, err := d2parser.ParseMapKey(key)
    return err == nil && len(mk.Edges) <= 1
}

Try / catch

err := d2oracle.Set(g, nil, key, &val)
if err != nil && err.Error() == "can only set one edge at a time" {
    return fmt.Errorf("split chain key %q into indexed single-edge keys", key)
}

Prevention

When it happens

Trigger: Calling d2oracle.Set or d2oracle.Create with a key containing a chain of two or more edges, e.g. `x.(a -> b -> c).style.opacity` or `(a <-> b <-> c)`.

Common situations: Users typing chained edges in a UI that forwards the raw key to Set; code generators that compose chains from path data; copying a valid D2 document key into the oracle API without splitting it.

Related errors


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