d2lang/d2 · error

no available layout

Error message

no available layout

What it means

getLayout in d2lib returns this error when CompileOptions.Layout is nil, meaning no layout engine name was supplied for the compilation. Without a layout, the graph cannot be arranged, so compilation aborts.

Source

Thrown at d2lib/d2.go:196

			return nil, nil, err
		}
		d.Scenarios = append(d.Scenarios, ld)
	}
	previous := geometry
	for i, l := range g.Steps {
		ld, stepGeometry, err := compileBoard(ctx, l, compileOpts, renderOpts, previous, i+1 < len(g.Steps))
		if err != nil {
			return nil, nil, err
		}
		d.Steps = append(d.Steps, ld)
		previous = stepGeometry
	}
	return d, geometry, nil
}

func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
	if opts.Layout == nil {
		return nil, errors.New("no available layout")
	}
	if opts.LayoutResolver == nil {
		return nil, fmt.Errorf("no layout resolver configured for layout engine %q", *opts.Layout)
	}
	return opts.LayoutResolver(*opts.Layout)
}

func getEdgeRouter(opts *CompileOptions) (d2graph.RouteEdges, error) {
	if opts.Layout != nil && opts.RouterResolver != nil {
		router, err := opts.RouterResolver(*opts.Layout)
		if err != nil {
			return nil, err
		}
		if router != nil {
			return router, nil
		}
	}
	return d2layouts.DefaultRouter, nil

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Set CompileOptions.Layout to a valid engine name, e.g. opts.Layout = &"dagre" or "elk".
  2. Use the default plugin resolution (dagre) if you want the default behavior — ensure your opts construction includes it.
  3. If relying on d2-config, verify vars.d2-config.layout-engine is set and propagated to CompileOptions.
  4. Check the LayoutResolver is also configured once Layout is set.

Example fix

// before
opts := &d2lib.CompileOptions{}
g, err := d2lib.Compile(ctx, ast, opts, nil)
// after
layout := "dagre"
opts := &d2lib.CompileOptions{Layout: &layout}
g, err := d2lib.Compile(ctx, ast, opts, nil)
Defensive patterns

Strategy: fallback

Validate before calling

if opts.Layout == nil {
  l := "dagre"
  opts.Layout = &l
}

Type guard

func layoutConfigured(opts *d2lib.CompileOptions) bool { return opts.Layout != nil }

Try / catch

g, err := d2lib.Compile(ctx, ast, opts, nil)
if err != nil && err.Error() == "no available layout" {
  l := "dagre"; opts.Layout = &l
  g, err = d2lib.Compile(ctx, ast, opts, nil)
}

Prevention

When it happens

Trigger: Calling d2lib.Compile (or compileBoard) with CompileOptions where the Layout *string field is unset and no default layout is applied by the caller.

Common situations: Programmatic compilation without setting opts.Layout (e.g. forgetting `d2plugin` default resolution); building CompileOptions by hand in tests/tools; a config path where d2-config layout-engine was not propagated.

Related errors


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