cargo_dist/
errors.rs

1//! Errors!
2//!
3//! This module is kind of pointless and stubbed out right now,
4//! because the crate is currently opting for a "typeless" approach
5//! (where everything gets folded into miette::Report right away).
6//!
7//! If we ever change this decision, this will be a lot more important!
8
9use axoproject::errors::AxoprojectError;
10use backtrace::Backtrace;
11use camino::Utf8PathBuf;
12use cargo_dist_schema::{target_lexicon::Triple, ArtifactId, TripleName};
13use color_backtrace::BacktracePrinter;
14use console::style;
15use miette::{Diagnostic, SourceOffset, SourceSpan};
16use thiserror::Error;
17
18/// An alias for the common Result type for this crate
19pub type DistResult<T> = std::result::Result<T, DistError>;
20
21/// Errors dist can have
22#[derive(Debug, Error, Diagnostic)]
23pub enum DistError {
24    /// random i/o error
25    #[error(transparent)]
26    Io(#[from] std::io::Error),
27
28    /// random axoasset error
29    #[error(transparent)]
30    #[diagnostic(transparent)]
31    Asset(#[from] axoasset::AxoassetError),
32
33    /// random axoprocess error
34    #[error(transparent)]
35    #[diagnostic(transparent)]
36    Cmd(#[from] axoprocess::AxoprocessError),
37
38    /// random gazenot error
39    #[error(transparent)]
40    #[diagnostic(transparent)]
41    Gazenot(#[from] gazenot::error::GazenotError),
42
43    /// random gazenot error
44    #[error(transparent)]
45    #[diagnostic(transparent)]
46    Project(#[from] axoproject::errors::ProjectError),
47
48    /// random string error
49    #[error(transparent)]
50    FromUtf8Error(#[from] std::string::FromUtf8Error),
51
52    /// random i/o error
53    #[error(transparent)]
54    Goblin(#[from] goblin::error::Error),
55
56    /// random camino conversion error
57    #[error(transparent)]
58    FromPathBufError(#[from] camino::FromPathBufError),
59
60    /// random dialoguer error
61    #[error(transparent)]
62    DialoguerError(#[from] dialoguer::Error),
63
64    /// random axotag error
65    #[error(transparent)]
66    #[diagnostic(transparent)]
67    AxotagError(#[from] axotag::errors::TagError),
68
69    /// random parseint error
70    #[error(transparent)]
71    ParseIntError(#[from] std::num::ParseIntError),
72
73    /// random triple parse error
74    #[error(transparent)]
75    TripleError(#[from] cargo_dist_schema::target_lexicon::ParseError),
76
77    /// A problem with a jinja template, which is always a dist bug
78    #[error("Failed to render template")]
79    #[diagnostic(
80        help("this is a bug in dist, let us know and we'll fix it: https://github.com/axodotdev/cargo-dist/issues/new")
81    )]
82    Jinja {
83        /// The SourceFile we were try to parse
84        #[source_code]
85        source: String,
86        /// The range the error was found on
87        #[label]
88        span: Option<miette::SourceSpan>,
89        /// Details of the error
90        #[source]
91        backtrace: JinjaErrorWithBacktrace,
92    },
93
94    /// Error from (cargo-)wix
95    #[error("WiX returned an error while building {msi}")]
96    Wix {
97        /// The msi we were trying to build
98        msi: String,
99        /// The underlying wix error
100        #[source]
101        details: wix::Error,
102    },
103
104    /// Error from (cargo-)wix init
105    #[error("Couldn't generate main.wxs for {package}'s msi installer")]
106    WixInit {
107        /// The package
108        package: String,
109        /// The underlying wix error
110        #[source]
111        details: wix::Error,
112    },
113
114    /// Error parsing metadata in Cargo.toml (json because it's from cargo-metadata)
115    #[error("Malformed metadata.dist in\n{manifest_path}")]
116    #[diagnostic(help("you can find a reference for the configuration schema at https://axodotdev.github.io/cargo-dist/book/reference/config.html"))]
117    CargoTomlParse {
118        /// path to file
119        manifest_path: Utf8PathBuf,
120        /// Inner error
121        #[source]
122        cause: serde_json::Error,
123    },
124
125    /// User declined to update dist, refuse to make progress
126    #[error("to update your dist config you must use the version your project is configured for")]
127    #[diagnostic(help(
128        "you're running {running_version} but the project is configured for {project_version}"
129    ))]
130    NoUpdateVersion {
131        /// Version the config had
132        project_version: semver::Version,
133        /// Version they're running
134        running_version: semver::Version,
135    },
136
137    /// User tried to enable Github CI support but had inconsistent urls for the repo
138    #[error("Github CI support requires your crates to agree on the URL of your repository")]
139    CantEnableGithubUrlInconsistent {
140        /// inner error that caught this
141        #[diagnostic_source]
142        inner: AxoprojectError,
143    },
144
145    /// User tried to enable Github CI support but no url for the repo
146    #[error("Github CI support requires you to specify the URL of your repository")]
147    #[diagnostic(help(
148        r#"Set the repository = "https://github.com/..." key in these manifests: {manifest_list}"#
149    ))]
150    CantEnableGithubNoUrl {
151        /// List of manifests to change
152        manifest_list: String,
153    },
154
155    /// We got a repository URL but couldn't interpret it as a GitHub repo
156    #[error("GitHub CI support requires a GitHub repository")]
157    CantEnableGithubUrlNotGithub {
158        /// inner error that caught this
159        #[diagnostic_source]
160        inner: AxoprojectError,
161    },
162
163    /// User supplied an illegal npm scope
164    #[error("The npm-scope field must be an all-lowercase value; the supplied value was {scope}")]
165    ScopeMustBeLowercase {
166        /// The incorrectly-formatted scope
167        scope: String,
168    },
169
170    /// Completely unknown format to install-path
171    ///
172    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
173    /// a serde_json error, reducing it to a String. So we inline the help!
174    #[error(r#"install-path = "{path}" has an unknown format (it can either be "CARGO_HOME", "~/subdir/", or "$ENV_VAR/subdir/")"#)]
175    InstallPathInvalid {
176        /// The full value passed to install-path
177        path: String,
178    },
179
180    /// Being pedantic about the env-var mode of install-path to be consistent
181    ///
182    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
183    /// a serde_json error, reducing it to a String. So we inline the help!
184    #[error(r#"install-path = "{path}" is missing a subdirectory (add a trailing slash if you want no subdirectory)"#)]
185    InstallPathEnvSlash {
186        /// The full value passed to install-path
187        path: String,
188    },
189
190    /// Being pedantic about the home mode of install-path to be consistent
191    ///
192    /// NOTE: we can't use `diagnostic(help)` here because this will get crammed into
193    /// a serde_json error, reducing it to a String. So we inline the help!
194    #[error(r#"install-path = "{path}" is missing a subdirectory (installing directly to home isn't allowed)"#)]
195    InstallPathHomeSubdir {
196        /// The full value passed to install-path
197        path: String,
198    },
199
200    /// explicitly requested workspace builds, but had packages with custom feature settings
201    #[error("precise-builds = false was set, but some packages have custom build features, making it impossible")]
202    #[diagnostic(help("these packages customized either features, no-default-features, or all-features:\n{packages:#?}"))]
203    PreciseImpossible {
204        /// paths of problem manifests
205        packages: Vec<camino::Utf8PathBuf>,
206    },
207
208    /// packages disagreed on homebrew taps
209    #[error("different homebrew taps were set in your workspace, this is currently unsupported")]
210    #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
211    MismatchedTaps {
212        /// paths of problem manifests
213        packages: Vec<camino::Utf8PathBuf>,
214    },
215
216    /// packages disagreed on publishers
217    #[error("different publisher settings were in your workspace, this is currently unuspported")]
218    #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
219    MismatchedPublishers {
220        /// paths of problem manifests
221        packages: Vec<camino::Utf8PathBuf>,
222    },
223
224    /// publishers disagreed on prereleases
225    #[error("different publisher 'prereleases' settings were in your workspace, this is currently unsupported")]
226    MismatchedPrereleases,
227
228    /// parse_tag concluded there was nothing to release
229    #[error("This workspace doesn't have anything for dist to Release!")]
230    NothingToRelease {
231        /// full help printout (very dynamic)
232        #[help]
233        help: String,
234    },
235
236    /// parse_tag concluded there are too many unrelated things for a single tag
237    #[error("There are too many unrelated apps in your workspace to coherently Announce!")]
238    TooManyUnrelatedApps {
239        /// full help printout (very dynamic)
240        #[help]
241        help: String,
242    },
243
244    /// Not an error; indicates that a file's contents differ via --check
245    #[error("{} has out of date contents and needs to be regenerated:\n{diff}", file.origin_path())]
246    #[diagnostic(help("run 'dist init' to update the file\n('allow-dirty' in Cargo.toml to ignore out of date contents)"))]
247    CheckFileMismatch {
248        /// The file whose contents differ
249        file: axoasset::SourceFile,
250        /// The diff
251        diff: String,
252    },
253
254    /// `dist generate` was passed an explicit GenerateMode but the config in their Cargo.toml
255    /// has that mode set to allow-dirty, a contradiction!
256    #[error("'{generate_mode}' is marked as allow-dirty in your dist config, refusing to run")]
257    ContradictoryGenerateModes {
258        /// The problematic mode
259        generate_mode: crate::config::GenerateMode,
260    },
261
262    /// msi/pkg with too many packages
263    #[error("{artifact_name} depends on multiple packages, which isn't yet supported")]
264    #[diagnostic(help("depends on {spec1} and {spec2}"))]
265    MultiPackage {
266        /// Name of the artifact
267        artifact_name: ArtifactId,
268        /// One of the packages
269        spec1: String,
270        /// A different package
271        spec2: String,
272    },
273
274    /// msi/pkg with too few packages
275    #[error("{artifact_name} has no binaries")]
276    #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
277    NoPackage {
278        /// Name of the msi
279        artifact_name: ArtifactId,
280    },
281
282    /// These GUIDs for msi's are required and enforced by `dist generate --check`
283    #[error("missing WiX GUIDs in {manifest_path}: {keys:?}")]
284    #[diagnostic(help("run 'dist init' to generate them"))]
285    MissingWixGuids {
286        /// The Cargo.toml missing them
287        manifest_path: Utf8PathBuf,
288        /// The missing keys
289        keys: &'static [&'static str],
290    },
291
292    /// unrecognized job style
293    #[error("{style} is not a recognized job value")]
294    #[diagnostic(help("Jobs that do not come with dist should be prefixed with ./"))]
295    UnrecognizedJobStyle {
296        /// value provided
297        style: String,
298    },
299
300    /// unrecognized hosting style
301    #[error("{style} is not a recognized release host")]
302    UnrecognizedHostingStyle {
303        /// value provided
304        style: String,
305    },
306
307    /// unrecognized hosting style
308    #[error("No GitHub hosting is defined!")]
309    #[diagnostic(help("Releases must have at least GitHub hosting for updates to be supported."))]
310    NoGitHubHosting {},
311
312    /// unrecognized ci style
313    #[error("{style} is not a recognized ci provider")]
314    UnrecognizedCiStyle {
315        /// value provided
316        style: String,
317    },
318
319    /// unrecognized library style
320    #[error("{style} is not a recognized type of library")]
321    UnrecognizedLibraryStyle {
322        /// value provided
323        style: String,
324    },
325
326    /// Linkage report can't be run for this combination of OS and target
327    #[error("unable to run linkage report for {target} on {host}")]
328    LinkageCheckInvalidOS {
329        /// The OS the check was run on
330        host: String,
331        /// The OS being checked
332        target: TripleName,
333    },
334
335    /// Linkage report can't be run for this target
336    #[error("unable to run linkage report for this type of binary")]
337    LinkageCheckUnsupportedBinary,
338
339    /// Error parsing a string containing an environment variable
340    /// in VAR=value syntax
341    #[error("Unable to parse environment variable as a key/value pair: {line}")]
342    #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
343    EnvParseError {
344        /// The line of text that couldn't be parsed
345        line: String,
346    },
347
348    /// An error running `git archive`
349    #[error("We failed to generate a source tarball for your project")]
350    #[diagnostic(help("This is probably not your fault, please file an issue!"))]
351    GitArchiveError {},
352
353    /// An error running `git -C path rev-parse HEAD`
354    #[error("We failed to query information about the git submodule at\n{path}")]
355    #[diagnostic(help("Does a submodule exist at that path? Has it been fetched with `git submodule update --init`?"))]
356    GitSubmoduleCommitError {
357        /// The path we failed to fetch
358        path: String,
359    },
360
361    /// A required tool is missing
362    #[error("{tool}, required to run this task, is missing")]
363    #[diagnostic(help("Ensure {tool} is installed"))]
364    ToolMissing {
365        /// the name of the missing tool
366        tool: String,
367    },
368
369    /// One or more required tools are missing.
370    #[error("The following tools are required to run this task, but are missing:\n- {}", tools.join("\n- "))]
371    #[diagnostic(help("Please install the tools mentioned above and try again."))]
372    EnvToolsMissing {
373        /// the names of the missing tools
374        tools: Vec<String>,
375    },
376
377    /// Unknown target requested
378    #[error(
379        "A build was requested for {target}, but the standalone updater isn't available for it."
380    )]
381    #[diagnostic(help("At the moment, we can only provide updater binaries for the core set of most common target triples. Please set `install-updater = false` in your config."))]
382    NoAxoupdaterForTarget {
383        /// The target triple being built for
384        target: String,
385    },
386
387    /// reqwest returned non-2xx/404 when checking axoupdater releases
388    #[error("Failed to check the latest release of axoupdater")]
389    #[diagnostic(help(
390        "Is your internet connection working? If not, this may be a bug; please file an issue!"
391    ))]
392    AxoupdaterReleaseCheckFailed {},
393
394    /// Failed to determine how to uncompress something
395    #[error("Failed to determine compression format")]
396    #[diagnostic(help("File extension of unrecognized file was {extension}"))]
397    UnrecognizedCompression {
398        /// The file extension of the unrecognized file
399        extension: String,
400    },
401
402    /// Binaries were missing
403    #[error("failed to find bin {bin_name} for {pkg_name}")]
404    #[diagnostic(help("did the above build fail?"))]
405    MissingBinaries {
406        /// Name of package
407        pkg_name: String,
408        /// Name of binary
409        bin_name: String,
410    },
411
412    /// Error during `dist selfupdate`
413    #[error("`dist selfupdate` failed; the new version isn't in the place we expected")]
414    #[diagnostic(help("This is probably not your fault, please file an issue!"))]
415    UpdateFailed {},
416
417    /// Trying to run dist selfupdate in a random dir
418    #[error("`dist selfupdate` needs to be run in a project")]
419    #[diagnostic(help("If you just want to update dist and not your project, pass --skip-init"))]
420    UpdateNotInWorkspace {
421        /// The report about the missing workspace
422        #[diagnostic_source]
423        cause: axoproject::errors::ProjectError,
424    },
425
426    /// Trying to include CargoHome with other install paths
427    #[error("Incompatible install paths configured in Cargo.toml")]
428    #[diagnostic(help("The CargoHome `install-path` configuration can't be combined with other install path strategies."))]
429    IncompatibleInstallPathConfiguration,
430
431    /// Passed --artifacts but no --target
432    #[error("You specified --artifacts, disabling host mode, but specified no targets to build!")]
433    #[diagnostic(help("try adding --target={host_target}"))]
434    CliMissingTargets {
435        /// Current host target
436        host_target: TripleName,
437    },
438
439    /// Workspace isn't init
440    #[error("please run 'dist init' before running any other commands!")]
441    NeedsInit,
442
443    /// Running different version from config
444    #[error("You're running dist {running_version}, but 'cargo-dist-version = {config_version}' is set in your Cargo.toml")]
445    #[diagnostic(help("Rerun 'dist init' to update to this version."))]
446    MismatchedDistVersion {
447        /// config version
448        config_version: String,
449        /// running version
450        running_version: String,
451    },
452
453    /// Failed to make sense of 'cargo -vV'
454    #[error("Failed to get get toolchain version from 'cargo -vV'")]
455    FailedCargoVersion,
456
457    /// Failed to parse Github repo pair
458    #[error("Failed to parse github repo: {pair}")]
459    #[diagnostic(help("should be 'owner/repo' format"))]
460    GithubRepoPairParse {
461        /// The input
462        pair: String,
463    },
464
465    /// Unknown permission specified in GitHub Actions config
466    #[error("One or more unrecognized permissions levels were specified: {levels:?}")]
467    #[diagnostic(help("recognized values are: admin, write, read"))]
468    GithubUnknownPermission {
469        /// The input
470        levels: Vec<String>,
471    },
472
473    /// An unknown target was found
474    #[error("Unrecognized target: {target}")]
475    #[diagnostic(help("The full list of supported targets can be found here: https://axodotdev.github.io/cargo-dist/book/reference/config.html#targets"))]
476    UnrecognizedTarget {
477        /// The target in question
478        target: TripleName,
479    },
480
481    /// Installers requested despite having nothing to install
482    #[error("Installers were requested, but app contains no installable binaries")]
483    #[diagnostic(help(
484        "The only installable files are libraries, but `install-libraries` isn't enabled."
485    ))]
486    EmptyInstaller {},
487
488    /// Configuration value for github-build-setup defined but not found
489    #[error("failed to load github-build-setup file")]
490    GithubBuildSetupNotFound {
491        /// Inner i/o error with file path
492        #[diagnostic_source]
493        details: axoasset::AxoassetError,
494    },
495
496    /// Configuration value for github-build-setup defined but not found
497    #[error("github-build-setup file wasn't valid yaml")]
498    GithubBuildSetupParse {
499        /// Inner parse error with path and spans
500        #[diagnostic_source]
501        details: axoasset::AxoassetError,
502    },
503
504    /// github-build-setup file contents are invalid
505    #[error("github-build-setup file at {file_path} was invalid: {message}")]
506    #[diagnostic(help(
507        "For more details about writing build steps see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid"
508    ))]
509    GithubBuildSetupNotValid {
510        /// The value from the configuration file
511        file_path: Utf8PathBuf,
512        /// Error message details
513        message: String,
514    },
515
516    /// Something has metadata.dist but shouldn't
517    #[error("The metadata.dist entry in this Cargo.toml isn't being used:\n{manifest_path}")]
518    #[diagnostic(help(
519        "You probably want to move them to the [dist] section in\n{dist_manifest_path}"
520    ))]
521    UnusedMetadata {
522        /// The manifest that had the metadata.dist
523        manifest_path: Utf8PathBuf,
524        /// The dist.toml/dist-workspace.toml that means it's ignored
525        dist_manifest_path: Utf8PathBuf,
526    },
527
528    /// Build command looks like they put args in same string as command
529    #[error("Your build-command's arguments need to be split up\n{manifest}\ncommand was: [\"{command}\"]")]
530    #[diagnostic(help("the command should be split [\"like\", \"--this\", \"--array=here\"]"))]
531    SusBuildCommand {
532        /// path to manifest
533        manifest: Utf8PathBuf,
534        /// what the command was
535        command: String,
536    },
537
538    /// missing "dist" script in a package.json
539    #[error("package.json was missing a \"dist\" script\n{manifest}")]
540    #[diagnostic(help(
541        "https://axodotdev.github.io/cargo-dist/book/quickstart/javascript.html#adding-a-dist-script"
542    ))]
543    NoDistScript {
544        /// path to package.json
545        manifest: Utf8PathBuf,
546    },
547
548    /// Unsupported cross-compilation host/target combination
549    #[error("Cross-compilation from {host} to {target} is not supported")]
550    #[diagnostic(help("{details}"))]
551    UnsupportedCrossCompile {
552        /// The host system
553        host: Triple,
554        /// The target system
555        target: Triple,
556        /// Additional details about why this combination is unsupported
557        details: String,
558    },
559
560    /// Cannot use cross-compilation with cargo-auditable
561    #[error(
562        "Cross-compilation builds from {host} to {target} cannot be used with cargo-auditable"
563    )]
564    #[diagnostic(help("set cargo-auditable to false or don't do cross-compilation"))]
565    CannotDoCargoAuditableAndCrossCompile {
566        /// The host system
567        host: Triple,
568        /// The target system
569        target: Triple,
570    },
571
572    /// Generic build with Cargo-only build options
573    #[error("You're building a generic package but have a Cargo-only option enabled")]
574    #[diagnostic(help("Please disable the following from your configuration: {}", options.join(", ")))]
575    CargoOnlyBuildOptions {
576        /// The names of the invalid options
577        options: Vec<String>,
578    },
579
580    /// missing "build-command" for a package that needs one
581    #[error("dist package was missing a build-command\n{manifest}")]
582    #[diagnostic(help(
583        "https://axodotdev.github.io/cargo-dist/book/quickstart/everyone-else.html#setup"
584    ))]
585    NoBuildCommand {
586        /// path to manifest
587        manifest: Utf8PathBuf,
588    },
589
590    /// cargo package with build-command
591    #[error(
592        "cargo package was overridden with a build-command, which isn't supported yet\n{manifest}"
593    )]
594    UnexpectedBuildCommand {
595        /// path to manifest
596        manifest: Utf8PathBuf,
597    },
598
599    /// Failure to decode base64-encoded certificate
600    #[error("We failed to decode the certificate stored in the CODESIGN_CERTIFICATE environment variable.")]
601    #[diagnostic(help("Is the value of this environment variable valid base64?"))]
602    CertificateDecodeError {},
603
604    /// Missing configuration for a .pkg
605    #[error("A Mac .pkg installer was requested, but the config is missing")]
606    #[diagnostic(help("Please ensure a dist.mac-pkg-config section is present in your config. For more details see: https://example.com"))]
607    MacPkgConfigMissing {},
608
609    /// User left identifier empty in init
610    #[error("No bundle identifier was specified")]
611    #[diagnostic(help("Please either enter a bundle identifier, or disable the Mac .pkg"))]
612    MacPkgBundleIdentifierMissing {},
613
614    /// Project depends on a too-old axoupdater
615    #[error("Your project ({package_name}) uses axoupdater as a library, but the version specified ({your_version}) is older than the minimum supported version ({minimum}). (The dependency comes via {source_name} in the dependency tree.)")]
616    #[diagnostic(help(
617        "https://axodotdev.github.io/cargo-dist/book/installers/updater.html#minimum-supported-version-checking"
618    ))]
619    AxoupdaterTooOld {
620        /// Name of the package
621        package_name: String,
622        /// The package that depended on axoupdater
623        source_name: String,
624        /// Minimum supported version
625        minimum: semver::Version,
626        /// Version the project uses
627        your_version: semver::Version,
628    },
629}
630
631impl From<minijinja::Error> for DistError {
632    fn from(details: minijinja::Error) -> Self {
633        let source: String = details.template_source().unwrap_or_default().to_owned();
634        let span = details.range().map(|r| r.into()).or_else(|| {
635            details.line().map(|line| {
636                // some minijinja errors only have a line, not a range, so let's just highlight the whole line
637                let start = SourceOffset::from_location(&source, line, 0);
638                let end = SourceOffset::from_location(&source, line + 1, 0);
639                let len = (end.offset() - start.offset()).wrapping_sub(1);
640                SourceSpan::from((start, len))
641            })
642        });
643
644        DistError::Jinja {
645            source,
646            span,
647            backtrace: JinjaErrorWithBacktrace::new(details),
648        }
649    }
650}
651/// A struct that implements `std::error::Error` so it can be added as "related" to
652/// a miette diagnostic, and it'll show the backtrace.
653#[derive(Debug)]
654pub struct JinjaErrorWithBacktrace {
655    /// The original minijinja error
656    pub error: minijinja::Error,
657    /// The captured backtrace
658    backtrace: Backtrace,
659}
660
661impl JinjaErrorWithBacktrace {
662    fn new(error: minijinja::Error) -> Self {
663        Self {
664            error,
665            backtrace: Backtrace::new_unresolved(),
666        }
667    }
668}
669
670impl std::error::Error for JinjaErrorWithBacktrace {}
671
672impl std::fmt::Display for JinjaErrorWithBacktrace {
673    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674        let mut bt = self.backtrace.clone();
675        bt.resolve();
676        let backtrace = BacktracePrinter::new()
677            .add_frame_filter(Box::new(|frames| {
678                // try to find index of a frame that says `dist::real_main` and remove everything after it
679                if let Some(real_main_idx) = frames.iter().position(|f| {
680                    f.name
681                        .as_ref()
682                        .map(|n| n.contains("real_main"))
683                        .unwrap_or(false)
684                }) {
685                    frames.splice(real_main_idx + 1.., vec![]);
686                }
687            }))
688            .format_trace_to_string(&bt)
689            .unwrap();
690        write!(
691            f,
692            "Backtrace:\n{}\n{}",
693            style(&backtrace).dim(),
694            style(&self.error).red()
695        )
696    }
697}