d2lang/d2 · error
failed to unmarshal json: %w
Error message
failed to unmarshal json: %w
What it means
After Flags runs the plugin successfully, its stdout must be a JSON array of PluginSpecificFlag. This error wraps json.Unmarshal failures, meaning the plugin emitted non-JSON or schema-mismatched output.
Source
Thrown at d2plugin/exec.go:67
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
cmd := exec.CommandContext(ctx, p.path, "flags")
defer xdefer.Errorf(&err, "failed to run %v", cmd.Args)
stdout, err := cmd.Output()
if err != nil {
ee := &exec.ExitError{}
if errors.As(err, &ee) && len(ee.Stderr) > 0 {
return nil, fmt.Errorf("%v\nstderr:\n%s", ee, ee.Stderr)
}
return nil, err
}
var flags []PluginSpecificFlag
err = json.Unmarshal(stdout, &flags)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal json: %w", err)
}
return flags, nil
}
func (p *execPlugin) HydrateOpts(opts []byte) error {
if opts != nil {
var execOpts map[string]interface{}
err := json.Unmarshal(opts, &execOpts)
if err != nil {
return xmain.UsageErrorf("non-exec layout options given for exec")
}
allString := make(map[string]string)
for k, v := range execOpts {
switch vt := v.(type) {
case string:
allString[k] = vtView on GitHub (pinned to 0d69dca6f5)
Solutions
- Check the raw stdout of the plugin's flags subcommand for stray non-JSON output
- Rebuild/reinstall the plugin to match the current d2 plugin protocol
- Fix the plugin so logs go to stderr and only JSON goes to stdout
Example fix
// before (in plugin)
fmt.Println("loading flags...")
json.NewEncoder(os.Stdout).Encode(flags)
// after
fmt.Fprintln(os.Stderr, "loading flags...")
json.NewEncoder(os.Stdout).Encode(flags) Defensive patterns
Strategy: try-catch
Validate before calling
out, err := exec.Command(pluginPath, "flags").Output()
if err == nil && !json.Valid(out) {
return fmt.Errorf("plugin flags output is not valid JSON")
} Type guard
func isUnmarshalError(err error) bool {
return strings.Contains(err.Error(), "failed to unmarshal json")
} Try / catch
flags, err := plugin.Flags()
if err != nil {
if strings.Contains(err.Error(), "failed to unmarshal json") {
return fmt.Errorf("plugin %s emits invalid JSON on stdout; reinstall it", plugin.Path)
}
return err
} Prevention
- Plugins must log to stderr, never stdout
- Pin plugin versions alongside d2 versions
- Validate plugin output in a CI smoke test
When it happens
Trigger: plugin.Flags() when the plugin's flags subcommand prints logs, warnings, or malformed JSON to stdout instead of the expected PluginSpecificFlag JSON array.
Common situations: Plugin writing human-readable logging to stdout instead of stderr; older plugin binary emitting an outdated flag schema.
Related errors
- failed to unmarshal input to graph: %s
- failed to unmarshal input graph to graph: %s
- failed to unmarshal Range from %q: missing End field
- failed to unmarshal Range from %q: missing Start field
- failed to unmarshal Position from %q: expected three fields
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/21d1f80d19fee8fe.
Report an issue: GitHub.