charmbracelet/crush · error
failed to tail log file: %v
Error message
failed to tail log file: %v
What it means
followLogs opens the crush.log file with the hpcloud/tail library's tail.TailFile (Follow:false, ReOpen:false). If the file cannot be opened or a tailer cannot be created (missing file, permission denied, path is a directory), the error is wrapped with this message. Note the follow path uses Follow:false here, so it reads currently available lines only.
Source
Thrown at internal/cmd/logs.go:85
}
return showLogs(logsFile, tailLines)
},
}
func init() {
logsCmd.Flags().BoolP("follow", "f", false, "Follow log output")
logsCmd.Flags().IntP("tail", "t", defaultTailLines, "Show only the last N lines default: 1000 for performance")
}
func followLogs(ctx context.Context, logsFile string, tailLines int) error {
t, err := tail.TailFile(logsFile, tail.Config{
Follow: false,
ReOpen: false,
Logger: tail.DiscardingLogger,
})
if err != nil {
return fmt.Errorf("failed to tail log file: %v", err)
}
var lines []string
for line := range t.Lines {
if line.Err != nil {
continue
}
lines = append(lines, line.Text)
if len(lines) > tailLines {
lines = lines[len(lines)-tailLines:]
}
}
t.Stop()
for _, line := range lines {
printLogLine(line)
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Verify the log file exists and is readable: ls -l <data-dir>/logs/crush.log; fix permissions with chmod if needed.
- Ensure you are passing the correct --data-dir / --cwd so the resolved path points to a real crush project.
- Re-run the crush project once to (re)generate the log file, then view logs.
- If developing, re-check the file after os.Stat and fall back to a friendly 'no logs' message instead of the raw tail error.
Example fix
// before
if _, err = os.Stat(logsFile); os.IsNotExist(err) {
log.Warn("no logs")
return nil
}
// after: also handle open failure gracefully
if _, err = os.Stat(logsFile); os.IsNotExist(err) {
log.Warn("Looks like you are not in a crush project. No logs found.")
return nil
}
if err := os.Chmod(logsFile, 0o644); err != nil { /* inspect */ } Defensive patterns
Strategy: validation
Validate before calling
// Go: ensure the log file exists and is readable before tailing
if fi, err := os.Stat(logsFile); err != nil || fi.IsDir() {
return fmt.Errorf("log file unavailable: %s", logsFile)
}
if f, err := os.Open(logsFile); err != nil {
return fmt.Errorf("log file unreadable: %w", err)
} else {
f.Close()
} Try / catch
t, err := tail.TailFile(logsFile, tail.Config{Follow: false, ReOpen: false, Logger: tail.DiscardingLogger})
if err != nil {
if os.IsNotExist(errors.Unwrap(err)) || errors.Is(err, fs.ErrNotExist) {
log.Warn("No logs found.")
return nil
}
return fmt.Errorf("failed to tail log file: %w", err)
} Prevention
- Confirm the crush project has run at least once so the log exists.
- Pass the correct --cwd/--data-dir to resolve the right log path.
- Check permissions on <data-dir>/logs/crush.log.
- Handle fs.ErrNotExist distinctly from other tail failures.
When it happens
Trigger: Calling followLogs with a logsFile path that does not exist (log file deleted between the os.Stat check and the tail), is unreadable due to permissions, or is a directory; also exotic cases like the file being on a filesystem tail cannot poll.
Common situations: Running `crush logs` right after cleaning the data directory; log file permissions changed by another process; custom --data-dir pointing somewhere without a logs/crush.log; race between project shutdown and log viewing.
Related errors
- failed to create stdout log file: %v
- failed to create stderr log file: %v
- failed to change directory: %v
- failed to crawl for stats: %w
- failed to gather stats from projects: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/883f969a0771b174.
Report an issue: GitHub.