charmbracelet/vhs · error · parser.Error

Expected path after Source

Error message

Expected path after Source

What it means

parseSource requires a STRING path token after the Source directive. If absent, it records "Expected path after Source" and returns immediately. Source includes another .tape file, so a path argument is mandatory.

Source

Thrown at parser/parser.go:676

	if p.peek.Type != token.STRING {
		p.errors = append(p.errors, NewError(p.peek, p.cur.Literal+" expects string"))
	}

	cmd.Args = p.peek.Literal
	p.nextToken()

	return cmd
}

// parseSource parses source command.
// Source command takes a tape path to include in current tape.
//
//	Source <path>
func (p *Parser) parseSource() []Command {
	cmd := Command{Type: token.SOURCE}

	if p.peek.Type != token.STRING {
		p.errors = append(p.errors, NewError(p.cur, "Expected path after Source"))
		p.nextToken()
		return []Command{cmd}
	}

	srcPath := p.peek.Literal

	// Check if path has .tape extension
	ext := filepath.Ext(srcPath)
	if ext != ".tape" {
		p.errors = append(p.errors, NewError(p.peek, "Expected file with .tape extension"))
		p.nextToken()
		return []Command{cmd}
	}

	// Check if tape exist
	if _, err := os.Stat(srcPath); os.IsNotExist(err) { //nolint:gosec
		notFoundErr := fmt.Sprintf("File %s not found", srcPath)
		p.errors = append(p.errors, NewError(p.peek, notFoundErr))

View on GitHub (pinned to c073383b5d)

Solutions

  1. Add a quoted path: `Source "other.tape"`
  2. Remove the Source line if it is unused
  3. Confirm the path token is double-quoted so the lexer emits a STRING

Example fix

// before
Source other.tape
// after
Source "other.tape"
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(line) == "Source" {
    return fmt.Errorf("Source requires a quoted .tape path")
}
if strings.HasPrefix(line, "Source ") && !strings.Contains(line, `"`) {
    return fmt.Errorf("Source path must be double-quoted")
}

Type guard

func sourceHasPath(t token.Token) bool { return t.Type == token.STRING && t.Literal != "" }

Try / catch

if err := parse(tape); err != nil && strings.Contains(err.Error(), "Expected path after Source") {
    return fmt.Errorf("bad Source directive in %s: %w", tape, err)
}

Prevention

When it happens

Trigger: A .tape file containing bare `Source` with no argument, or an unquoted token (e.g. `Source other.tape` without quotes) so the path token is not of type STRING when parseSource runs.

Common situations: Copy-pasting `Source` examples without quotes; forgetting the argument when commenting out the path; concatenating lines so the path lands on the wrong line.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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