color-backtrace makes a Rust panic readable with color and source snippets
Colorful panic backtraces for Rust
At a glance
- What is it?
- color-backtrace is a Rust library that installs a panic hook that prints a colorized backtrace with the relevant source snippets, separates your frames from dependency frames, and hides the noise around the panic point. It also offers an opt-in backend that cuts its transitive dependency count from around twelve to two, at the cost of relying on an unstable standard library format.
- Who is it for?
- color-backtrace suits any Rust binary whose users will ever see a panic in a terminal, since a single install call replaces the default backtrace with something that separates your code from its dependencies and points at the failing lines.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 83 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What a panic looks like before and after the hook
The library is described in one line as making panics a little less painful by nicely colorizing them and printing the relevant source snippets. That is the whole idea, and the audience is Rust developers whose programs crash in front of users rather than in front of a compiler.
A Rust panic printout with backtraces enabled shows a message and a list of frames, but it does not distinguish your code from the standard library, from your dependencies, or from the frames that were already on the stack before the panic. Reading that output under pressure is work. color-backtrace takes over the panic output and reorganises it so the frames that matter stand out.
The feature list in the README names five things: colorize backtraces to be easier on the eyes, show source snippets if source files are found on disk, print frames of application code versus dependencies in different color, hide all the frames after the panic was already initiated, and hide language runtime initialization frames.
The last two are the ones that make it feel like a different tool rather than a recoloured version of the same output. A panic that unwinds through a deep call stack produces a lot of frames that carry no information, and removing them is what turns a wall of addresses into a short list of the calls that led to the failure.
Adding the dependency and installing the hook
Adding the crate to a Cargo.toml is the first step:
[dependencies]
color-backtrace = { version = "0.7" }Then a single call in your app initialization code installs it:
color_backtrace::install();That is the whole integration for most applications. There is no handler to register, no builder to configure and no global state to manage beyond this call, which matters because the hook is global and the README is explicit that configuration changes are made through the printer object rather than through a configuration file.
The version string in the Cargo.toml snippet is what the README shows, while the manifest in the repository is at 0.7.4 with edition 2021 and a rust-version of 1.81. The crate requires that toolchain floor, so a project pinned to an older compiler will not build it.
The examples directory shows the range of situations the library is meant for, with separate files for a failed assertion, a custom message, formatting the backtrace to a string with and without the standard library backend, forcing color output, the panic macro, and an unwrapped Result. Those files are the closest thing the repository has to a usage catalogue, and they map directly onto the decisions a user actually makes.
Customising the message and choosing the output stream
If the default output is not what you want, the README shows a second form that uses the BacktracePrinter directly:
use color_backtrace::{default_output_stream, BacktracePrinter};
BacktracePrinter::new().message("Custom message!").install(default_output_stream());The builder pattern matters here. Instead of a bare install call, you get an object you can configure and then hand an output stream to, and the message method sets the text shown above the backtrace. The default_output_stream function supplies the standard stream, which you can replace if you need the backtrace to go somewhere other than the usual error output.
This split is the practical reason to depend on the library rather than copying its code into your project. The two documented entry points cover the two realistic cases: a quick install for an application that wants nicer output, and a configured printer for an application that needs a different message or a different destination for the text.
What the builder does not offer is documented anywhere else in the README. There is no mention of turning individual features off, of changing the colours, of filtering by module path, or of configuring how many frames are printed. If you need one of those, the source in src/ is the place to look, and the README will not tell you whether the option exists.
The filtering rules that decide which frames survive
The interesting behaviour is in what the printer removes, and the README describes it in plain terms.
Frames after the panic was already initiated are hidden. When a panic unwinds, the stack still holds the frames of the functions that were running, including the ones higher up that were not involved in the failure. Those are noise for the person reading the output, and the printer cuts them.
Language runtime initialization frames are hidden too, which removes the noise at the bottom of the stack that belongs to the language itself rather than to the program.
Frames of application code and frames of dependencies are printed in different color, so the boundary between your code and someone else's is visible without reading every line.
Source snippets are shown if the source files are found on disk. That conditional is the one detail worth planning around. The snippet can only be printed if the machine running the binary still has the source file at the path the compiler recorded, so a release build shipped without its sources, or a container image that dropped the source tree, will produce a backtrace with no snippets even though the frames are correct.
So the library degrades rather than fails. You still get frames and colors, you just lose the part that points at the failing line.
The btparse backend trades stability for two dependencies
The default backend links the backtrace crate, which is the reliable path. The README also documents an alternative capture backend, btparse, which can be selected through a Cargo feature:
[dependencies]
color-backtrace = {
version = "0.7",
default-features = false,
features = ["use-btparse-crate"],
}The payoff is stated plainly: this reduces the number of transitive dependencies from around 12 to just 2. The two dependency trees are shown side by side. The default route pulls in backtrace and its own tree of addr2line, gimli, cfg-if, libc, miniz_oxide, object, rustc-demangle and cc, while the btparse route brings in btparse and termcolor:
$ cargo tree --no-default-features --features=use-btparse-crate
color-backtrace v0.6.1 (/Users/ath/Development/color-backtrace)
├── btparse v0.2.0 (https://github.com/yaahc/btparse.git?rev=54f9ddb8c7c8f8e034226fdcacab93cd76e1453b#54f9ddb8)
└── termcolor v1.4.1The reason this is not the default is the stability tradeoff the README is unusually candid about. btparse relies on the undocumented and unstable Debug implementation of std::backtrace::Backtrace to remain unchanged. The README notes this has been untouched for 4+ years as of writing, but there is no guarantee that it will always work.
That is the trade: a much smaller dependency tree against a dependency on an implementation detail of the standard library that the Rust project owes nobody stability for. The manifest also marks the use-backtrace-crate feature as preferred over btparse if both are enabled, so the safe backend wins by default even when both features are present.
Tests need a constructor because Rust has no test init hook
Using the library in a test suite is the awkward part, and the README explains why before offering a fix. Defining custom init functions that run before tests are started is currently not supported in Rust, and initializing color-backtrace in each and every test is tedious even when it is wrapped into a function.
The suggested solution is the ctor crate. Somewhere, preferably in your crate's main module, you add a constructor function:
#[cfg(test)]
mod tests {
use ctor::ctor;
#[ctor]
fn init_color_backtrace() {
color_backtrace::install();
}
}You can also put the same code outside a #[cfg(test)] section, in which case the panic handler is installed for both test and regular runs. The choice between the two placements is about scope: inside the test module the hook applies only when the tests run, while outside it the application always has the colorized handler installed.
Note the extra dependency this brings in. ctor is not part of color-backtrace, so the advice for a project that was trying to keep its dependency count low is to take the btparse backend for the library and add ctor for the test-only case. Whether a test-only dependency is acceptable in your build is your call, since the hook it installs is global to the test binary.
NO_COLOR, FORCE_COLOR and the tty check
Colour output is controllable through environment variables, which is what makes the library usable in a CI log or a redirected stream.
NO_COLOR, when set to any value, disables all colors in the backtrace output. FORCE_COLOR, when set to any value, forces colors even when output is redirected. When both are present, NO_COLOR takes precedence and colors are disabled.
If neither variable is provided, colors are automatically applied when stderr is attached to a tty. That default is the sensible behaviour for a terminal session and the correct behaviour for a log file, since escape sequences in a captured log are noise.
The implementation follows the NO_COLOR specification, linked in the README. The precedence rule is worth internalising as a general habit rather than a color-backtrace quirk: an explicit request for no color wins over an explicit request for color, which is the convention the wider ecosystem has settled on.
For an application that ships a command line tool, this means the same binary behaves sensibly in three situations without any configuration. Redirected output loses the escapes, an interactive terminal keeps them, and a user with NO_COLOR set in their environment always gets plain text.
Version, licence and the shape of the upgrade
The crate is at version 0.7.4 in the Cargo.toml, and the repository has no GitHub releases, so the crates.io version is the artifact you install and the changelog in the CHANGELOG.md file at the repository root is the record of what changed. The last push was on 2026-08-01, and the repository is not archived.
Licensing is unusually friendly for a Rust library. The manifest declares the licence as MIT OR Apache-2.0, and the repository root carries both LICENSE-MIT and LICENSE-APACHE, with matching badges in the README. The dual licence means you can pick whichever of the two terms suits your organisation, which matters for corporate codebases that have a policy against one of them.
The upgrade path is small. The library is a build-time dependency of your binary, the public surface is two calls, and the features that change behaviour are Cargo features rather than configuration. The Cargo.toml also carries a resolve-modules feature, described as printing module memory mappings and taking effect only on Linux, which is enabled together with the use-backtrace-crate feature.
What you give up by taking the default path is the smaller dependency tree. What you give up by taking the btparse path is a guarantee. The manifest states that the backtrace route is reliable and preferred, so the safe choice is also the default one.
Editorial conclusion
color-backtrace suits any Rust binary whose users will ever see a panic in a terminal, since a single install call replaces the default backtrace with something that separates your code from its dependencies and points at the failing lines. It is the wrong choice for a library crate that must not impose a panic handler on its callers, for a project that cannot accept a panic hook at all, or for anyone who wants the dependency count kept minimal without taking the btparse stability risk. Verify first that the source files are actually shipped to the machine where the panic happens, because snippets are only shown when the source is found on disk, then confirm that your error reporting pipeline captures the colored output rather than stripping it.
Frequently asked questions
How do I add color-backtrace to a Rust project and enable it?
Add color-backtrace = { version = "0.7" } under [dependencies] in your Cargo.toml, then call color_backtrace::install() in your app initialization code. The crate needs Rust 1.81 or newer and uses edition 2021.
How do I set a custom panic message with color-backtrace?
Use the BacktracePrinter instead of the plain install call: create one with BacktracePrinter::new(), set the message, and install it with default_output_stream(). The same call form lets you send the output to a different stream.
How do I reduce the transitive dependencies of color-backtrace?
Disable default features and enable the use-btparse-crate feature, which swaps the backtrace crate for btparse and cuts the transitive count from around 12 to 2. The README warns that btparse relies on the undocumented and unstable Debug implementation of std::backtrace::Backtrace.
How can I control colored output in color-backtrace?
Set NO_COLOR to disable colors, or FORCE_COLOR to force them even when output is redirected, and NO_COLOR wins when both are set. With neither variable present, colors are applied automatically when stderr is attached to a tty.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/athre0z-color-backtrace)Was this useful?