charmbracelet/vhs · error · parser.Error
Expected file with .png extension
Error message
Expected file with .png extension
What it means
This parser error is raised by the Screenshot command parser when its argument path does not end in the `.png` extension. The library only supports saving screenshots as PNG files, so during parsing of `Screenshot <path>` it validates `filepath.Ext(path) == ".png"` and appends this error to the parser's error list otherwise. The offending path is not accepted as the command's argument, so the Screenshot command is effectively dropped from the parsed output.
Source
Thrown at parser/parser.go:770
// parseScreenshot parses screenshot command.
// Screenshot command takes a file path for storing screenshot.
//
// Screenshot <path>
func (p *Parser) parseScreenshot() Command {
cmd := Command{Type: token.SCREENSHOT}
if p.peek.Type != token.STRING {
p.errors = append(p.errors, NewError(p.cur, "Expected path after Screenshot"))
p.nextToken()
return cmd
}
path := p.peek.Literal
// Check if path has .png extension
ext := filepath.Ext(path)
if ext != ".png" {
p.errors = append(p.errors, NewError(p.peek, "Expected file with .png extension"))
p.nextToken()
return cmd
}
cmd.Args = path
p.nextToken()
return cmd
}
// Errors returns any errors that occurred during parsing.
func (p *Parser) Errors() []Error {
return p.errors
}
// nextToken gets the next token from the lexer
// and updates the parser tokens accordingly.
func (p *Parser) nextToken() {View on GitHub (pinned to c073383b5d)
Solutions
- Rename the Screenshot target to use a `.png` extension, e.g. change `Screenshot "shot.jpg"` to `Screenshot "shot.png"`.
- If the file must be another format, save it as .png first and convert afterwards (e.g. with ImageMagick: `magick shot.png shot.jpg`) instead of asking the Screenshot command for that format.
- Check the exact literal passed to Screenshot for typos or case issues such as `.PNG` or `.Png`; normalize the extension to lowercase `.png`.
- Inspect `parser.Errors()` after parsing your script and fix every reported line rather than only this occurrence, since parseScreenshot returns early and skips adding the argument to the command.
- If the path comes from a variable or template, print/interpolate it to verify the extension is actually `.png` in the final script.
Example fix
// before type "Enter" Screenshot "output/demo.jpg" // after type "Enter" Screenshot "output/demo.png"
Defensive patterns
Strategy: validation
Validate before calling
for _, path := range screenshotPaths {
if filepath.Ext(path) != ".png" {
return fmt.Errorf("screenshot path %q must end in .png", path)
}
}
// now safe to run the script through the parser Type guard
func isPNGPath(p string) bool {
return strings.EqualFold(filepath.Ext(p), ".png")
} Prevention
- Always name Screenshot targets with a lowercase `.png` extension when writing script/tape files.
- Pre-validate all screenshot paths in your script files before handing them to the parser, rejecting any whose filepath.Ext is not ".png".
- After parsing, always check `parser.Errors()` rather than assuming the parse succeeded; this error is reported there, not via panic.
- If your tooling generates the scripts, template screenshot output names with a hard-coded ".png" suffix.
- Convert non-PNG outputs after capture (png -> jpg etc.) rather than requesting non-PNG formats from the Screenshot command.
When it happens
Trigger: Calling `Parser.Parse` on a script containing a `Screenshot` directive whose argument is a quoted or bare path with any extension other than `.png` (e.g. `Screenshot "out.jpg"`, `Screenshot shot.jpeg`, `Screenshot capture` with no extension). The check happens at parse time in parseScreenshot, before any command executes.
Common situations: Hand-written or generated tape/command files where the screenshot output was given as .jpg/.jpeg/.gif/.bmp/.webp or omitted the extension entirely; refactoring scripts that changed output format; tools or scripts that template a screenshot path from a variable set to a non-PNG filename.
Related errors
- Expected file with .tape extension
- Source tape: ${srcPath} is empty
- Expected path after Screenshot
- Invalid command: ${literal}
- Wait+ expects Line or Screen
AI-assisted analysis of charmbracelet/vhs@c073383b5d (2026-09-02).
Data as JSON: /api/errors/479383e470694a90.
Report an issue: GitHub.