{"record":{"id":"303fcfcb18f08a6b","repo":"dbt-labs/dbt-core","slug":"unhandled-panic-set-multiple-times","errorCode":null,"errorMessage":"`unhandled_panic` set multiple times.","messagePattern":"`unhandled_panic` set multiple times\\.","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"crates/dbt-runtime-macros/src/entry.rs","lineNumber":183,"sourceCode":"        Ok(())\n    }\n\n    fn set_crate_name(&mut self, name: syn::Lit, span: Span) -> Result<(), syn::Error> {\n        if self.crate_name.is_some() {\n            return Err(syn::Error::new(span, \"`crate` set multiple times.\"));\n        }\n        let name_path = parse_path(name, span, \"crate\")?;\n        self.crate_name = Some(name_path);\n        Ok(())\n    }\n\n    fn set_unhandled_panic(\n        &mut self,\n        unhandled_panic: syn::Lit,\n        span: Span,\n    ) -> Result<(), syn::Error> {\n        if self.unhandled_panic.is_some() {\n            return Err(syn::Error::new(\n                span,\n                \"`unhandled_panic` set multiple times.\",\n            ));\n        }\n\n        let unhandled_panic = parse_string(unhandled_panic, span, \"unhandled_panic\")?;\n        let unhandled_panic =\n            UnhandledPanic::from_str(&unhandled_panic).map_err(|err| syn::Error::new(span, err))?;\n        self.unhandled_panic = Some((unhandled_panic, span));\n        Ok(())\n    }\n\n    fn macro_name(&self) -> &'static str {\n        if self.is_test {\n            \"dbt_runtime::test\"\n        } else {\n            \"dbt_runtime::main\"\n        }","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-runtime-macros/src/entry.rs#L165-L201","documentation":"The `unhandled_panic` option may be given at most once per #[tokio::main]/#[tokio::test]. set_unhandled_panic checks its stored Option and raises a syn::Error at the duplicate key if it is already set, before even validating the value.","triggerScenarios":"#[tokio::test(flavor = \"current_thread\", unhandled_panic = \"ignore\", unhandled_panic = \"shutdown_runtime\")] — duplicate `unhandled_panic` key in the attribute.","commonSituations":"Refactoring test attributes and leaving the old value in place; generators emitting the option twice; merging two tests' attribute lines.","solutions":["Keep a single `unhandled_panic = \"...\"` key","Pick one behavior: `ignore` or `shutdown_runtime`","Remove the option to accept the default behavior"],"exampleFix":"// before\n#[tokio::test(unhandled_panic = \"ignore\", unhandled_panic = \"shutdown_runtime\")]\nasync fn t() {}\n\n// after\n#[tokio::test(unhandled_panic = \"shutdown_runtime\")]\nasync fn t() {}","handlingStrategy":"validation","validationCode":"fn check_no_dupes(attr_src: &str, key: &str) -> Result<(), String> {\n    let n = attr_src.matches(&format!(\"{} =\", key)).count();\n    if n <= 1 { Ok(()) } else { Err(format!(\"duplicate `{key}`\")) }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only add unhandled_panic when you have a specific reason; default is usually fine","Don't copy test attributes between tests blindly — dedupe keys after pasting","Keep one behavior choice documented per test suite"],"tags":["rust","proc-macro","tokio","duplicate-key"],"backgroundTag":"conflicting-config-options","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}