facebook/relay · error
unknown @catch `to` value. Use `NULL` or `RESULT` (default)
Error message
unknown @catch `to` value. Use `NULL` or `RESULT` (default) instead.
What it means
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.
Source
Thrown at compiler/crates/relay-transforms/src/catch_directive.rs:58
pub static CATCH_DIRECTIVE_NAME: LazyLock<DirectiveName> =
LazyLock::new(|| DirectiveName(intern!("catch")));
pub static NULL_TO: LazyLock<StringKey> = LazyLock::new(|| intern!("NULL"));
pub static RESULT_TO: LazyLock<StringKey> = LazyLock::new(|| intern!("RESULT"));
pub static TO_ARGUMENT: LazyLock<ArgumentName> = LazyLock::new(|| ArgumentName(intern!("to")));
// Possible @catch `to` enum values ordered by severity.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug, Hash)]
pub enum CatchTo {
Null,
Result,
}
impl From<StringKey> for CatchTo {
fn from(to: StringKey) -> Self {
match to {
_ if to == *RESULT_TO => Self::Result,
_ if to == *NULL_TO => Self::Null,
_ => panic!("unknown @catch `to` value. Use `NULL` or `RESULT` (default) instead."),
}
}
}
impl From<CatchTo> for StringKey {
fn from(val: CatchTo) -> Self {
match val {
CatchTo::Null => *NULL_TO,
CatchTo::Result => *RESULT_TO,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CatchMetadataDirective {
pub to: CatchTo,
}
associated_data_impl!(CatchMetadataDirective);View on GitHub (pinned to 668b1b85e0)
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.
Example fix
// before field @catch(to: "null") // after field @catch(to: "NULL")
Defensive patterns
Strategy: validation
Validate before calling
const VALID_CATCH_TO = new Set(['NULL', 'RESULT']);
if (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}`); } Type guard
const isValidCatchTo = (v) => v === 'NULL' || v === 'RESULT';
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- @__metadata directive should have only one argument!
- Duplicate fragment definitions named {}: first one: {:?} s
- @__metadata directive expect only constant argument values.
- The {} argument in exec_time_resolvers directive should be t
- Expected at most one handle directive, got `{handle_field_di
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/bf7dcd5a70237d79.
Report an issue: GitHub.