d2lang/d2 · error

could not find center of %s. Is it declared as an actor?

Error message

could not find center of %s. Is it declared as an actor?

What it means

routeMessages needs the horizontal center X of each message endpoint to draw the sequence lifeline arrows. getCenter returned nil for the message's Src, meaning that object has no computed center — typically because it was never registered as an actor.

Source

Thrown at d2layouts/d2sequence/sequence_diagram.go:605

// routeMessages routes horizontal edges (messages) from Src to Dst lifeline (actor/span center)
// in another step, routes are adjusted to spans borders when necessary
func (sd *sequenceDiagram) routeMessages() error {
	messageOffset := sd.maxActorHeight + sd.yStep
	for _, message := range sd.messages {
		message.ZIndex = MESSAGE_Z_INDEX
		noteOffset := 0.
		for _, note := range sd.notes {
			if sd.verticalIndices[note.AbsID()] < sd.verticalIndices[message.AbsID()] {
				noteOffset += note.Height + sd.yStep
			}
		}

		var startX, endX float64
		if startCenter := getCenter(message.Src); startCenter != nil {
			startX = startCenter.X
		} else {
			return fmt.Errorf("could not find center of %s. Is it declared as an actor?", message.Src.ID)
		}
		if endCenter := getCenter(message.Dst); endCenter != nil {
			endX = endCenter.X
		} else {
			return fmt.Errorf("could not find center of %s. Is it declared as an actor?", message.Dst.ID)
		}
		isToDescendant := strings.HasPrefix(message.Dst.AbsID(), message.Src.AbsID()+".")
		isFromDescendant := strings.HasPrefix(message.Src.AbsID(), message.Dst.AbsID()+".")
		isSelfMessage := message.Src == message.Dst

		currSrc := message.Src
		for !currSrc.Parent.IsSequenceDiagram() {
			currSrc = currSrc.Parent
		}
		currDst := message.Dst
		for !currDst.Parent.IsSequenceDiagram() {
			currDst = currDst.Parent
		}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Declare the source endpoint as a top-level actor in the sequence diagram
  2. Move the message endpoint to an actor shape instead of a nested child
  3. Check for typos in the edge's source key so it matches a declared actor
  4. Reproduce with the object's AbsID to confirm which shape is missing

Example fix

// before
outer.shape -> b
// after
outer -> b
Defensive patterns

Strategy: validation

Validate before calling

for _, msg := range messages {
	if !isDeclaredActor(msg.Src) {
		return fmt.Errorf("source %s must be a declared actor", msg.Src.AbsID())
	}
}

Type guard

func isDeclaredActor(obj *d2graph.Object) bool {
	return obj != nil && obj.Parent != nil && obj.Parent.IsSequenceDiagram()
}

Try / catch

if err := layout(...); err != nil {
	if strings.Contains(err.Error(), "Is it declared as an actor?") {
		return fmt.Errorf("check sequence endpoints: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: A message (edge) in the sequence diagram references a Src object that getCenter cannot find among laid-out actors — e.g. the source is a child/descendant shape not added as an actor during the walk.

Common situations: Connecting to a shape that lives inside a group/box rather than a top-level actor; messages referencing shapes declared after layout skipped them; typos causing the Src key to resolve to an unexpected object.

Related errors


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