d2lang/d2 · error

multiboard output cannot be written to stdout

Error message

multiboard output cannot be written to stdout

What it means

When the compiled diagram has subboards (layers/scenarios/steps), render produces a multiboard output consisting of multiple files. Because stdout ('-') can only hold one document, render rejects writing multiboard output to stdout with this error.

Source

Thrown at d2cli/main.go:834

	if !ok {
		return in, nil
	}
	return postProcessor.PostProcess(ctx, in)
}

func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plugin d2plugin.Plugin, opts d2svg.RenderOpts, inputPath, outputPath string, bundle, forceAppendix bool, browser playwright.Browser, ruler *textmeasure.Ruler, diagram *d2target.Diagram, ext exportExtension, asciiMode string) ([][]byte, error) {
	if diagram.Name != "" {
		ext := filepath.Ext(outputPath)
		outputPath = strings.TrimSuffix(outputPath, ext)
		outputPath = filepath.Join(outputPath, diagram.Name)
		outputPath += ext
	}

	boardOutputPath := outputPath
	if len(diagram.Layers) > 0 || len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0 {
		if outputPath == "-" {
			// TODO it can if composed into one
			return nil, fmt.Errorf("multiboard output cannot be written to stdout")
		}
		// Boards with subboards must be self-contained folders.
		ext := filepath.Ext(boardOutputPath)
		boardOutputPath = strings.TrimSuffix(boardOutputPath, ext)
		os.RemoveAll(boardOutputPath)
		boardOutputPath = filepath.Join(boardOutputPath, "index")
		boardOutputPath += ext
	}

	layersOutputPath := outputPath
	if len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0 {
		ext := filepath.Ext(layersOutputPath)
		layersOutputPath = strings.TrimSuffix(layersOutputPath, ext)
		layersOutputPath = filepath.Join(layersOutputPath, "layers")
		layersOutputPath += ext
	}
	scenariosOutputPath := outputPath
	if len(diagram.Layers) > 0 || len(diagram.Steps) > 0 {

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Provide a real output file path so boards are written as a folder (dir/index files)
  2. Remove the layers/scenarios/steps if a single-board stdout render was intended
  3. Flatten the diagram to a single board before rendering to stdout

Example fix

// before
d2 --board . input.d2 - | tee out.svg   // multiboard diagram
// after
d2 input.d2 outdir/   # or render a single board to stdout
Defensive patterns

Strategy: validation

Validate before calling

// before writing to stdout, ensure single board
if outputPath == "-" && (len(diagram.Layers) > 0 || len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0) {
    return errors.New("provide a file path for multiboard output")
}

Try / catch

if err := render(...); err != nil && strings.Contains(err.Error(), "multiboard") {
    // retry with a directory output path instead of "-"
}

Prevention

When it happens

Trigger: Running d2 with output path '-' (stdout) on a diagram containing layers, scenarios, or steps, and an output format that is not a single composed document.

Common situations: Piping d2 output in shell scripts while the .d2 file defines layers, forgetting that multiboard needs a directory output path.

Related errors


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