{"record":{"id":"c86ea3f9a18d212b","repo":"d2lang/d2","slug":"multiboard-output-cannot-be-written-to-stdout","errorCode":null,"errorMessage":"multiboard output cannot be written to stdout","messagePattern":"multiboard output cannot be written to stdout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2cli/main.go","lineNumber":834,"sourceCode":"\tif !ok {\n\t\treturn in, nil\n\t}\n\treturn postProcessor.PostProcess(ctx, in)\n}\n\nfunc 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) {\n\tif diagram.Name != \"\" {\n\t\text := filepath.Ext(outputPath)\n\t\toutputPath = strings.TrimSuffix(outputPath, ext)\n\t\toutputPath = filepath.Join(outputPath, diagram.Name)\n\t\toutputPath += ext\n\t}\n\n\tboardOutputPath := outputPath\n\tif len(diagram.Layers) > 0 || len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0 {\n\t\tif outputPath == \"-\" {\n\t\t\t// TODO it can if composed into one\n\t\t\treturn nil, fmt.Errorf(\"multiboard output cannot be written to stdout\")\n\t\t}\n\t\t// Boards with subboards must be self-contained folders.\n\t\text := filepath.Ext(boardOutputPath)\n\t\tboardOutputPath = strings.TrimSuffix(boardOutputPath, ext)\n\t\tos.RemoveAll(boardOutputPath)\n\t\tboardOutputPath = filepath.Join(boardOutputPath, \"index\")\n\t\tboardOutputPath += ext\n\t}\n\n\tlayersOutputPath := outputPath\n\tif len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0 {\n\t\text := filepath.Ext(layersOutputPath)\n\t\tlayersOutputPath = strings.TrimSuffix(layersOutputPath, ext)\n\t\tlayersOutputPath = filepath.Join(layersOutputPath, \"layers\")\n\t\tlayersOutputPath += ext\n\t}\n\tscenariosOutputPath := outputPath\n\tif len(diagram.Layers) > 0 || len(diagram.Steps) > 0 {","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2cli/main.go#L816-L852","documentation":"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.","triggerScenarios":"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.","commonSituations":"Piping d2 output in shell scripts while the .d2 file defines layers, forgetting that multiboard needs a directory output path.","solutions":["Provide a real output file path so boards are written as a folder (dir/index files)","Remove the layers/scenarios/steps if a single-board stdout render was intended","Flatten the diagram to a single board before rendering to stdout"],"exampleFix":"// before\nd2 --board . input.d2 - | tee out.svg   // multiboard diagram\n// after\nd2 input.d2 outdir/   # or render a single board to stdout","handlingStrategy":"validation","validationCode":"// before writing to stdout, ensure single board\nif outputPath == \"-\" && (len(diagram.Layers) > 0 || len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0) {\n    return errors.New(\"provide a file path for multiboard output\")\n}","typeGuard":null,"tryCatchPattern":"if err := render(...); err != nil && strings.Contains(err.Error(), \"multiboard\") {\n    // retry with a directory output path instead of \"-\"\n}","preventionTips":["Never use '-' as output path for diagrams that define layers/scenarios/steps","In scripts, default to a temp directory output when board count > 1","Check for subboards before choosing stdout mode"],"tags":["go","cli","multiboard","stdout"],"backgroundTag":"multiboard-stdout-unsupported","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}