{"record":{"id":"99fad3b96917f195","repo":"charmbracelet/vhs","slug":"expected-path-after-screenshot","errorCode":null,"errorMessage":"Expected path after Screenshot","messagePattern":"Expected path after Screenshot","errorType":"validation","errorClass":"parser.Error","httpStatus":null,"severity":"error","filePath":"parser/parser.go","lineNumber":760,"sourceCode":"\t\t\tsrcCmd.Type == token.OUTPUT {\n\t\t\tcontinue\n\t\t}\n\t\tfiltered = append(filtered, srcCmd)\n\t}\n\n\tp.nextToken()\n\treturn filtered\n}\n\n// parseScreenshot parses screenshot command.\n// Screenshot command takes a file path for storing screenshot.\n//\n//\tScreenshot <path>\nfunc (p *Parser) parseScreenshot() Command {\n\tcmd := Command{Type: token.SCREENSHOT}\n\n\tif p.peek.Type != token.STRING {\n\t\tp.errors = append(p.errors, NewError(p.cur, \"Expected path after Screenshot\"))\n\t\tp.nextToken()\n\t\treturn cmd\n\t}\n\n\tpath := p.peek.Literal\n\n\t// Check if path has .png extension\n\text := filepath.Ext(path)\n\tif ext != \".png\" {\n\t\tp.errors = append(p.errors, NewError(p.peek, \"Expected file with .png extension\"))\n\t\tp.nextToken()\n\t\treturn cmd\n\t}\n\n\tcmd.Args = path\n\tp.nextToken()\n\n\treturn cmd","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/charmbracelet/vhs/blob/c073383b5de0b1f57bf514113029c306bc986539/parser/parser.go#L742-L778","documentation":"parseScreenshot requires a STRING path token after the Screenshot directive. If the next token is not a string, it records \"Expected path after Screenshot\" and returns. Screenshot writes a capture at that point in the recording, so a destination path is required.","triggerScenarios":"A .tape file with bare `Screenshot`, `Screenshot ./out.png` (unquoted), or `Screenshot 42` — parseScreenshot sees peek.Type != token.STRING.","commonSituations":"Forgetting the argument when adding mid-recording screenshots; using shell-style unquoted paths; editor auto-complete dropping the quotes.","solutions":["Add a quoted destination: `Screenshot \"out.png\"`","Remove the Screenshot line if not needed","Ensure the path is double-quoted so the lexer emits a STRING token"],"exampleFix":"// before\nScreenshot ./out.png\n// after\nScreenshot \"./out.png\"","handlingStrategy":"validation","validationCode":"line := `Screenshot \"out.png\"`\nif strings.HasPrefix(strings.TrimSpace(line), \"Screenshot\") {\n    rest := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), \"Screenshot\"))\n    if !strings.HasPrefix(rest, `\"`) {\n        return fmt.Errorf(\"Screenshot requires a quoted path: %s\", line)\n    }\n}","typeGuard":"func screenshotHasPath(t token.Token) bool { return t.Type == token.STRING && t.Literal != \"\" }","tryCatchPattern":"if err := parse(tape); err != nil && strings.Contains(err.Error(), \"Expected path after Screenshot\") {\n    return fmt.Errorf(\"bad Screenshot directive in %s: %w\", tape, err)\n}","preventionTips":["Always quote Screenshot destinations","Add mid-recording screenshots as complete lines with a path argument","Run a syntax check on tapes before recording"],"tags":["parser","syntax","screenshot"],"backgroundTag":"missing-required-argument","analyzedSha":"c073383b5de0b1f57bf514113029c306bc986539","analyzedAt":"2026-09-02T01:53:05.682Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}