{"record":{"id":"5a62d1788ae2d9c8","repo":"can1357/oh-my-pi","slug":"vhs-failed-to-render-the-gallery-screenshot-detai","errorCode":null,"errorMessage":"VHS failed to render the gallery screenshot${detail ? `: ${detail.slice(-600)}` : \"\"}","messagePattern":"VHS failed to render the gallery screenshot(.+?)` : \"\"\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/gallery-screenshot.ts","lineNumber":140,"sourceCode":"\tawait Bun.write(\n\t\ttapePath,\n\t\tbuildTape({\n\t\t\tgifPath,\n\t\t\toutPng: args.outPng,\n\t\t\tansiPath,\n\t\t\twidthPx,\n\t\t\theightPx,\n\t\t\tfont: args.font,\n\t\t\tfontSize: args.fontSize,\n\t\t\tthemeJson: args.themeJson,\n\t\t}),\n\t);\n\n\ttry {\n\t\tconst result = await Bun.$`${args.vhs} ${tapePath}`.quiet().nothrow();\n\t\tif (result.exitCode !== 0 || !(await Bun.file(args.outPng).exists())) {\n\t\t\tconst detail = result.stderr.toString().trim() || result.stdout.toString().trim();\n\t\t\tthrow new Error(`VHS failed to render the gallery screenshot${detail ? `: ${detail.slice(-600)}` : \"\"}`);\n\t\t}\n\t} finally {\n\t\tawait Promise.all([\n\t\t\tfs.promises.rm(ansiPath, { force: true }),\n\t\t\tfs.promises.rm(tapePath, { force: true }),\n\t\t\tfs.promises.rm(gifPath, { force: true }),\n\t\t]);\n\t}\n}\n\ninterface TapeArgs {\n\tgifPath: string;\n\toutPng: string;\n\tansiPath: string;\n\twidthPx: number;\n\theightPx: number;\n\tfont: string;\n\tfontSize: number;","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/gallery-screenshot.ts#L122-L158","documentation":"renderChunk invokes the resolved `vhs` binary on a generated .tape file and expects exit code 0 plus a non-empty output PNG. If either check fails it throws with the last 600 characters of VHS's captured stderr/stdout, after cleaning up the temporary tape/ansi/gif files in a finally block.","triggerScenarios":"VHS exits non-zero or fails to produce outPng — e.g. invalid tape directives, a PTY/font problem in headless environments, missing fonts, or out-of-memory/timeout during rendering.","commonSituations":"Running screenshots in CI containers without a working PTY or fonts; tape generation producing directives the installed VHS version rejects; disk space or permission problems preventing the PNG from being written.","solutions":["Read the detail suffix in the message — it contains VHS's own error output; fix the underlying issue it reports","Install/refresh fonts required for the configured screenshot font (`options.font`)","Upgrade VHS to the latest version (`brew upgrade vhs`) and retry","Run in an environment with a usable PTY (e.g. use `script -c` in CI containers) and confirm the output directory is writable"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { $which } from \"@oh-my-pi/pi-utils\";\nconst vhs = $which(\"vhs\");\nif (!vhs) throw new Error(\"vhs not installed\");\nif (!process.stdout.isTTY && process.env.CI) console.warn(\"headless render may fail without PTY/fonts\");","typeGuard":null,"tryCatchPattern":"try {\n  const pngs = await captureGalleryScreenshots(sections, opts);\n} catch (err) {\n  const detail = err instanceof Error ? err.message : String(err);\n  if (detail.startsWith(\"VHS failed to render\")) {\n    logger.error(\"VHS render failed — check VHS output in message\", { detail: detail.slice(-600) });\n    // fall back or rethrow with VHS's own detail\n  } else throw err;\n}","preventionTips":["Keep VHS up to date; tape directive syntax varies between versions","Install the fonts referenced by the screenshot font option in CI images","Run renders where a PTY is available; wrap with `script -c` in CI containers","Check free disk space and output-directory writability before rendering"],"tags":["cli","external-tool","rendering","subprocess"],"backgroundTag":"subprocess-render-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}