charmbracelet/vhs · error · parser.Error

Source tape: ${srcPath} is empty

Error message

Source tape: ${srcPath} is empty

What it means

After reading the file, parseSource checks len(srcTape) == 0 and reports "Source tape: <path> is empty" if the included tape has no content. An empty include contributes nothing and is treated as a mistake.

Source

Thrown at parser/parser.go:712

		p.errors = append(p.errors, NewError(p.peek, notFoundErr))
		p.nextToken()
		return []Command{cmd}
	}

	// Check if source tape contains nested Source command
	d, err := os.ReadFile(srcPath) //nolint:gosec
	if err != nil {
		readErr := fmt.Sprintf("Unable to read file: %s", srcPath)
		p.errors = append(p.errors, NewError(p.peek, readErr))
		p.nextToken()
		return []Command{cmd}
	}

	srcTape := string(d)
	// Check source tape is NOT empty
	if len(srcTape) == 0 {
		readErr := fmt.Sprintf("Source tape: %s is empty", srcPath)
		p.errors = append(p.errors, NewError(p.peek, readErr))
		p.nextToken()
		return []Command{cmd}
	}

	srcLexer := lexer.New(srcTape)
	srcParser := New(srcLexer)

	// Check not nested source
	srcCmds := srcParser.Parse()
	for _, cmd := range srcCmds {
		if cmd.Type == token.SOURCE {
			p.errors = append(p.errors, NewError(p.peek, "Nested Source detected"))
			p.nextToken()
			return []Command{cmd}
		}
	}

	// Check src errors

View on GitHub (pinned to c073383b5d)

Solutions

  1. Add valid tape instructions to the sourced file
  2. Remove the Source line if the include is not needed
  3. Verify file size with `wc -c` / `ls -l` before running

Example fix

// before
(empty file)
// after
echo 'Type "hello"' > empty.tape
Defensive patterns

Strategy: validation

Validate before calling

p := "nested.tape"
info, err := os.Stat(p)
if err == nil && info.Size() == 0 {
    return fmt.Errorf("Source target %s is empty", p)
}

Type guard

func tapeNonEmpty(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.Size() > 0
}

Try / catch

if err := parse(tape); err != nil && strings.Contains(err.Error(), "is empty") {
    return fmt.Errorf("populate or remove empty include: %w", err)
}

Prevention

When it happens

Trigger: Parse() on a tape sourcing a zero-byte .tape file (e.g. `touch empty.tape` then `Source "empty.tape"`), or a file truncated by a failed checkout/save.

Common situations: Placeholder files created but never filled in; interrupted writes; git LFS files not materialized (0 bytes).

Related errors


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