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

  1. Rename the Screenshot target to use a `.png` extension, e.g. change `Screenshot "shot.jpg"` to `Screenshot "shot.png"`.
  2. 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.
  3. Check the exact literal passed to Screenshot for typos or case issues such as `.PNG` or `.Png`; normalize the extension to lowercase `.png`.
  4. 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.
  5. 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

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


AI-assisted analysis of charmbracelet/vhs@c073383b5d (2026-09-02). Data as JSON: /api/errors/479383e470694a90. Report an issue: GitHub.