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] = vt

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Check the raw stdout of the plugin's flags subcommand for stray non-JSON output
  2. Rebuild/reinstall the plugin to match the current d2 plugin protocol
  3. 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

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


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