{"record":{"id":"bf7dcd5a70237d79","repo":"facebook/relay","slug":"unknown-catch-to-value-use-null-or-result","errorCode":null,"errorMessage":"unknown @catch `to` value. Use `NULL` or `RESULT` (default) instead.","messagePattern":"unknown @catch `to` value\\. Use `NULL` or `RESULT` \\(default\\) instead\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/catch_directive.rs","lineNumber":58,"sourceCode":"pub static CATCH_DIRECTIVE_NAME: LazyLock<DirectiveName> =\n    LazyLock::new(|| DirectiveName(intern!(\"catch\")));\npub static NULL_TO: LazyLock<StringKey> = LazyLock::new(|| intern!(\"NULL\"));\npub static RESULT_TO: LazyLock<StringKey> = LazyLock::new(|| intern!(\"RESULT\"));\npub static TO_ARGUMENT: LazyLock<ArgumentName> = LazyLock::new(|| ArgumentName(intern!(\"to\")));\n\n// Possible @catch `to` enum values ordered by severity.\n#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug, Hash)]\npub enum CatchTo {\n    Null,\n    Result,\n}\n\nimpl From<StringKey> for CatchTo {\n    fn from(to: StringKey) -> Self {\n        match to {\n            _ if to == *RESULT_TO => Self::Result,\n            _ if to == *NULL_TO => Self::Null,\n            _ => panic!(\"unknown @catch `to` value. Use `NULL` or `RESULT` (default) instead.\"),\n        }\n    }\n}\n\nimpl From<CatchTo> for StringKey {\n    fn from(val: CatchTo) -> Self {\n        match val {\n            CatchTo::Null => *NULL_TO,\n            CatchTo::Result => *RESULT_TO,\n        }\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct CatchMetadataDirective {\n    pub to: CatchTo,\n}\nassociated_data_impl!(CatchMetadataDirective);","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/catch_directive.rs#L40-L76","documentation":"CatchTo::from converts the string value of a @catch(to: ...) directive into the CatchTo enum. Only RESULT (default) and NULL are valid; any other string reaches the catch-all arm and panics. This is an internal invariant — the directive's literal values should be validated earlier.","triggerScenarios":"A @catch directive whose to argument parses to a StringKey that is neither RESULT nor NULL reaching the transform — i.e. a schema/validations gap allowed an unknown @catch(to:) value into the IR.","commonSituations":"Typo like @catch(to: \"NULL_\") or lowercase @catch(to: \"null\"); running a newer compiler against older validations or hand-crafted IR; upstream tools emitting non-standard @catch values.","solutions":["Change the directive to @catch(to: \"NULL\") or @catch(to: \"RESULT\") (or remove it for the default).","Watch letter casing — values are case-sensitive uppercase constants.","Update compiler/tooling versions so validation rejects invalid @catch values with a proper diagnostic.","Search your codebase for @catch usages and audit their to values."],"exampleFix":"// before\nfield @catch(to: \"null\")\n// after\nfield @catch(to: \"NULL\")","handlingStrategy":"validation","validationCode":"const VALID_CATCH_TO = new Set(['NULL', 'RESULT']);\nif (dir.name.value === 'catch') { const to = dir.arguments?.find(a => a.name.value === 'to'); if (to && !VALID_CATCH_TO.has(to.value.value)) throw new Error(`Invalid @catch to: ${to.value.value}`); }","typeGuard":"const isValidCatchTo = (v) => v === 'NULL' || v === 'RESULT';","tryCatchPattern":null,"preventionTips":["Use only uppercase NULL or RESULT in @catch(to:)","Lint/validate directives in CI before running relay-compiler","Audit @catch usages after copying code from other projects"],"tags":["graphql","directive","catch","panic"],"backgroundTag":"invalid-directive-argument","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}