d2lang/d2 · error

failed to unmarshal input graph to graph: %s

Error message

failed to unmarshal input graph to graph: %s

What it means

In routeEdges of the served plugin, both the input graph (in.G) and the edges graph (in.GEdges) are deserialized. This error means in.G failed to deserialize into a d2graph.Graph.

Source

Thrown at d2plugin/serve.go:175

	}
	return nil
}

func routeEdges(ctx context.Context, p RoutingPlugin, ms *xmain.State) error {
	inRaw, err := io.ReadAll(ms.Stdin)
	if err != nil {
		return err
	}

	var in routeEdgesInput
	err = json.Unmarshal(inRaw, &in)
	if err != nil {
		return err
	}

	var g d2graph.Graph
	if err := d2graph.DeserializeGraph(in.G, &g); err != nil {
		return fmt.Errorf("failed to unmarshal input graph to graph: %s", in)
	}

	var gedges d2graph.Graph
	if err := d2graph.DeserializeGraph(in.GEdges, &gedges); err != nil {
		return fmt.Errorf("failed to unmarshal input edges graph to graph: %s", in)
	}

	err = p.RouteEdges(ctx, &g, gedges.Edges)
	if err != nil {
		return err
	}

	b, err := d2graph.SerializeGraph(&g)
	if err != nil {
		return err
	}
	_, err = ms.Stdout.Write(b)
	if err != nil {

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Serialize the graph with d2graph.SerializeGraph before sending it as in.G
  2. Check host/plugin d2 version compatibility
  3. Dump in.G and validate it as JSON graph output

Example fix

// before
in := RouteEdgesInput{G: rawGraphString}
// after
b, _ := d2graph.SerializeGraph(g)
in := RouteEdgesInput{G: b}
Defensive patterns

Strategy: validation

Validate before calling

var probe map[string]any
if json.Unmarshal(in.G, &probe) != nil { /* in.G is not valid serialized graph JSON */ }

Type guard

func isValidRouteEdgesInput(in RouteEdgesInput) bool {
	var g d2graph.Graph
	return d2graph.DeserializeGraph(in.G, &g) == nil
}

Try / catch

if err := routeEdges(ctx, p, ms); err != nil {
	if strings.Contains(err.Error(), "failed to unmarshal input graph") {
		log.Printf("RouteEdgesInput.G was not a serialized d2 graph")
	}
}

Prevention

When it happens

Trigger: The routeedges RPC input's G field is empty, malformed, or not d2graph serialized JSON.

Common situations: Caller passing an unserialized graph, version skew between host and plugin changing serialization format, hand-crafted RPC payloads.

Related errors


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