charmbracelet/vhs · error · parser.Error
Nested Source detected
Error message
Nested Source detected
What it means
parseSource parses the included tape with a sub-parser and scans its commands; if any command is itself a SOURCE, it reports "Nested Source detected" and aborts. This library forbids source-within-source to keep include resolution one level deep and avoid recursion.
Source
Thrown at parser/parser.go:724
}
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
srcErrors := srcParser.Errors()
if len(srcErrors) > 0 {
p.errors = append(p.errors, NewError(p.peek, fmt.Sprintf("%s has %d errors", srcPath, len(srcErrors))))
p.nextToken()
return []Command{cmd}
}
filtered := make([]Command, 0)
for _, srcCmd := range srcCmds {
// Output have to be avoid in order to not overwrite output of the original tape.
if srcCmd.Type == token.SOURCE ||
srcCmd.Type == token.OUTPUT {View on GitHub (pinned to c073383b5d)
Solutions
- Flatten the nested include: inline the grandchild tape's contents into the child tape
- Remove the Source line from the included tape
- Restructure so each top-level tape sources only leaf tapes with no Source commands
Example fix
// child.tape before Source "base.tape" Type "hi" // after (base.tape commands pasted here) Type "hi"
Defensive patterns
Strategy: validation
Validate before calling
data, _ := os.ReadFile("child.tape")
for i, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(strings.TrimSpace(line), "Source ") {
return fmt.Errorf("child.tape line %d contains nested Source; flatten it", i+1)
}
} Type guard
func hasSourceCmd(cmds []Command) bool {
for _, c := range cmds {
if c.Type == token.SOURCE { return true }
}
return false
} Try / catch
if err := parse(tape); err != nil && strings.Contains(err.Error(), "Nested Source") {
return fmt.Errorf("flatten includes in %s: %w", tape, err)
} Prevention
- Keep include graphs one level deep: top-level tapes source only leaf tapes
- Grep shared partials for Source before including them
- Document that Source cannot be nested in your tape conventions
When it happens
Trigger: Parse() on a tape whose Source target itself contains a `Source "..."` line — the sub-parser emits a SOURCE command and the outer parser flags nesting.
Common situations: Refactoring tapes into shared partials where a partial also sources a common base; trying to build an include tree; combining generated tapes that contain Source lines.
Related errors
- Expected path after Source
- ${srcPath} has ${count} errors
- Invalid command: ${literal}
- Wait+ expects Line or Screen
- Wait expects positive duration
AI-assisted analysis of charmbracelet/vhs@c073383b5d (2026-09-02).
Data as JSON: /api/errors/fc25afa5cfc29d03.
Report an issue: GitHub.