d2lang/d2 · error

plugin has routing feature but does not implement RoutingPlu

Error message

plugin has routing feature but does not implement RoutingPlugin

What it means

The engine resolver checks that a plugin advertising the routing feature actually implements the d2plugin.RoutingPlugin interface. A plugin that claims hasRouter but fails the type assertion cannot satisfy route requests, so Run returns this error.

Source

Thrown at d2cli/main.go:481

		}

		pluginInfo, err := plugin.Info(ctx)
		if err != nil {
			return nil, err
		}
		hasRouter := false
		for _, feat := range pluginInfo.Features {
			if feat == d2plugin.ROUTES_EDGES {
				hasRouter = true
				break
			}
		}
		if !hasRouter {
			return nil, nil
		}
		routingPlugin, ok := plugin.(d2plugin.RoutingPlugin)
		if !ok {
			return nil, fmt.Errorf("plugin has routing feature but does not implement RoutingPlugin")
		}

		routeEdges := d2graph.RouteEdges(routingPlugin.RouteEdges)
		cached[engine] = routeEdges
		return routeEdges, nil
	}
}

func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, fs fs.FS, layout *string, renderOpts d2svg.RenderOpts, fontFamily *d2fonts.FontFamily, monoFontFamily *d2fonts.FontFamily, animateInterval int64, inputPath, outputPath string, boardPath []string, noChildren, bundle, forceAppendix bool, browser playwright.Browser, ext exportExtension, asciiMode string) (_ []byte, written bool, _ error) {
	// Use ELK layout for ascii outputs when layout is dagre or unspecified
	if ext == TXT {
		if layout == nil || *layout == "dagre" {
			if ms.Log.Debug != nil {
				prevLayout := "unspecified"
				if layout != nil {
					prevLayout = *layout
				}
				ms.Log.Debug.Printf("switching layout engine to ELK for ASCII format (was %s)", prevLayout)

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Update the plugin to implement d2plugin.RoutingPlugin including RouteEdges
  2. Use a different engine that fully implements the routing feature
  3. Fix plugin registration so the routing feature flag is not set unless the interface is implemented

Example fix

// before
type MyPlugin struct{} // has routing feature flag but no RouteEdges
// after
func (p *MyPlugin) RouteEdges(g *d2graph.Graph) error { /* ... */ return nil } // implement RoutingPlugin
Defensive patterns

Strategy: type-guard

Validate before calling

if rp, ok := plugin.(d2plugin.RoutingPlugin); !ok {
    // reject or disable routing feature for this plugin before use
}

Type guard

func supportsRouting(p d2plugin.Plugin) bool {
    _, ok := p.(d2plugin.RoutingPlugin)
    return ok
}

Try / catch

router, err := LayoutResolver(ctx, ms, plugins)(engine)
if err != nil {
    // fall back to a known-good engine that implements RoutingPlugin
}

Prevention

When it happens

Trigger: Requesting an engine whose plugin reports the routing capability but whose concrete type does not implement RoutingPlugin — i.e. a misregistered/incompatible custom plugin.

Common situations: Custom or third-party layout plugins that enable the routing feature flag without implementing RouteEdges, or version mismatches between plugin and d2 versions.

Related errors


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