epi052/feroxbuster · error
Could not print banner
Error message
Could not print banner
What it means
Before scanning, wrapped_main asks the banner system to check for updates and print the banner to stderr; if banner.print_to returns an error, it cleans up and bails with fmt_err("Could not print banner"). The update-check failure is deliberately ignored (let _), but an actual failure rendering/printing the banner aborts the run.
Solutions
- Ensure stderr is open and writable (check redirections like 2>/dev/null or closed fds)
- Run in a normal terminal or fix TERM/locale environment variables
- Update ferox — newer releases handle odd terminal states more gracefully
- If it persists, capture stderr to a file to see the underlying writer error and report it
Example fix
// before ferox -u https://t.com 2>&1- # closed stderr // after ferox -u https://t.com 2>ferox.log
Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.stderr.writable) throw new Error('stderr must be open and writable'); Try / catch
try { ... } catch (e) { if (String(e).includes('Could not print banner')) { /* check stderr redirection/terminal state; run with stderr redirected to a file */ } } Prevention
- Avoid closing or weirdly redirecting stderr when launching scans
- Run in environments with a writable stderr (fix CI/docker fds)
- Keep ferox updated for terminal-handling fixes
When it happens
Trigger: banner.print_to fails while writing the banner to the provided stderr handle — e.g. the terminal/stderr writer errors (closed descriptor, unsupported terminal state) or banner rendering fails on the given config.
Common situations: Running in restricted environments (CI, docker with closed stdout/stderr), weird TERM settings or non-TTY sinks, or output redirection to a file/pipe that is closed (broken pipe).
Related errors
AI-assisted analysis of epi052/feroxbuster@1f595dab5c (2026-09-13).
Data as JSON: /api/errors/9480342e891a541b.
Report an issue: GitHub.
Appendix: source
Thrown at src/main.rs:548
// wait until the parallel branch has been handled before sending the UpdateTargets command
// this ensures that only the targets being scanned are sent to the Stats object
//
// if sent before the parallel branch is handled, the Stats object will have duplicate
// targets
handles.stats.send(UpdateTargets(targets.clone()))?;
if matches!(config.output_level, OutputLevel::Default) {
// only print banner if output level is default (no banner on --quiet|--silent)
let std_stderr = stderr(); // std::io::stderr
let mut banner = Banner::new(&targets, &config);
// only interested in the side-effect that sets banner.update_status
let _ = banner.check_for_updates(UPDATE_URL, handles.clone()).await;
if banner.print_to(std_stderr, config.clone()).is_err() {
clean_up(handles, tasks).await?;
bail!(fmt_err("Could not print banner"));
}
}
{
let send_to_file = !config.output.is_empty();
// The TermOutHandler spawns a FileOutHandler, so errors in the FileOutHandler never bubble
// up due to the TermOutHandler never awaiting the result of FileOutHandler::start (that's
// done later here in main). sync checks that the tx/rx connection to the file handler works
if send_to_file && handles.output.sync(send_to_file).await.is_err() {
// output file specified and file handler could not initialize
clean_up(handles, tasks).await?;
let msg = format!("Couldn't start {} file handler", config.output);
bail!(fmt_err(&msg));
}
}
// discard non-responsive targetsView on GitHub (pinned to 1f595dab5c)