{"record":{"id":"c888b609d4ad7321","repo":"d2lang/d2","slug":"plugin-has-routing-feature-but-does-not-implement-c888b6","errorCode":null,"errorMessage":"plugin has routing feature but does not implement RoutingPlugin","messagePattern":"plugin has routing feature but does not implement RoutingPlugin","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2plugin/serve.go","lineNumber":63,"sourceCode":"\t\terr = HydratePluginOpts(ctx, ms, p)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tsubcmd := ms.Opts.Flags.Arg(0)\n\t\tswitch subcmd {\n\t\tcase \"info\":\n\t\t\treturn info(ctx, p, ms)\n\t\tcase \"flags\":\n\t\t\treturn flags(ctx, p, ms)\n\t\tcase \"layout\":\n\t\t\treturn layout(ctx, p, ms)\n\t\tcase \"postprocess\":\n\t\t\treturn postProcess(ctx, p, ms)\n\t\tcase \"routeedges\":\n\t\t\troutingPlugin, ok := p.(RoutingPlugin)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"plugin has routing feature but does not implement RoutingPlugin\")\n\t\t\t}\n\t\t\treturn routeEdges(ctx, routingPlugin, ms)\n\t\tdefault:\n\t\t\treturn xmain.UsageErrorf(\"unrecognized command: %s\", subcmd)\n\t\t}\n\t}\n}\n\nfunc info(ctx context.Context, p Plugin, ms *xmain.State) error {\n\tinfo, err := p.Info(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tb, err := json.Marshal(info)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = ms.Stdout.Write(b)","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2plugin/serve.go#L45-L81","documentation":"When d2plugin serves a plugin process and receives the routeedges command, it asserts the plugin implements the RoutingPlugin interface. This error means the plugin declares the routing feature but its Go type does not implement RouteEdges.","triggerScenarios":"Registering a plugin whose Info includes routing but whose plugin object only implements LayoutPlugin, then invoking the plugin binary with the routeedges subcommand.","commonSituations":"Custom plugin development: feature flags in Info() updated without extending the plugin type; copy-pasted plugin scaffolding missing the RouteEdges method.","solutions":["Add a RouteEdges(ctx, *d2graph.Graph) ([][]int, error) method to the plugin type implementing RoutingPlugin","Remove the routing feature from the plugin's Info if routing is not intended","Embed or delegate to a RoutingPlugin implementation"],"exampleFix":"// before\nfunc (p *myPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { ... }\n// after\nfunc (p *myPlugin) RouteEdges(ctx context.Context, g *d2graph.Graph) ([][]int, error) { ... } // implement RoutingPlugin","handlingStrategy":"type-guard","validationCode":"if _, ok := p.(d2plugin.RoutingPlugin); !ok {\n\t// don't advertise routing feature in Info()\n}","typeGuard":"rp, ok := p.(d2plugin.RoutingPlugin)\nif !ok {\n\treturn fmt.Errorf(\"plugin does not implement RoutingPlugin\")\n}\n_ = rp","tryCatchPattern":"if err := runPluginServe(p); err != nil {\n\tif strings.Contains(err.Error(), \"does not implement RoutingPlugin\") {\n\t\tlog.Fatal(\"fix plugin Info features or implement RouteEdges\")\n\t}\n}","preventionTips":["Keep Info() features in sync with implemented interfaces","Compile-time assertion: var _ d2plugin.RoutingPlugin = (*myPlugin)(nil)","Test serving the plugin binary with each subcommand"],"tags":["plugin","interface","routing"],"backgroundTag":"plugin-interface-not-implemented","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}