facebook/relay · error
Expected at least one of an @updatable reader AST, or normal
Error message
Expected at least one of an @updatable reader AST, or normalization AST to be present
What it means
generate_artifacts panics when an operation grouping has neither an @updatable reader AST nor a normalization AST. Every grouped operation must produce at least one of these artifacts; an empty group means an earlier compiler stage left inconsistent data.
Source
Thrown at compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs:177
// Therefore this must be an updatable query in order to continue.
if reader
.directives
.named(*UPDATABLE_DIRECTIVE)
.is_some()
{
let source_hash = source_hashes
.get(&reader.name.item.into())
.cloned()
.unwrap();
return generate_updatable_query_artifact(
ArtifactSourceKey::ExecutableDefinition(reader.name.item.into()),
project_config,
&operations,
source_hash,
)
}
}
panic!("Expected at least one of an @updatable reader AST, or normalization AST to be present");
})
.chain(programs.reader.fragments().map(|reader_fragment| {
let source_name = if let Some(client_edges_directive) =
ClientEdgeGeneratedQueryMetadataDirective::find(&reader_fragment.directives)
{
client_edges_directive.source_name.item
} else {
reader_fragment.name.item.into()
};
// If the fragment is generated for the RelayResolver model (id, or model instance)
// we need to update the source definition to include the original text of the resolver.
let source_hash = source_hashes.get(&source_name).cloned();
// We need this `if/else` here because of the way the compiler is handling the aritfacts
// deletion (see commit_project in compiler.rs).
// To remove the artifact, the artifact map should not contain any document/source that may
// generate the artifact. If we merge these sources (fragment name and resolver hash)
// then the removal of the source hash won't trigger the removal of the artifact, becauseView on GitHub (pinned to 668b1b85e0)
Solutions
- Delete generated artifacts/compiler cache and rerun the compiler
- Reduce the offending operation to a minimal repro and check which directives (@updatable etc.) it uses
- Pin/report against the Relay version — this panic usually indicates a compiler bug
Defensive patterns
Strategy: try-catch
Validate before calling
// Wrap compiler invocation in a process-level guard and retry a clean build
// Node wrapper example:
const { spawnSync } = require('child_process');
const r = spawnSync('relay-compiler', ['--watchman', 'false'], { stdio: 'inherit' });
if (r.status !== 0) {
require('fs').rmSync('__generated__', { recursive: true, force: true });
spawnSync('relay-compiler', [], { stdio: 'inherit' });
} Try / catch
try {
runRelayCompiler();
} catch (e) {
if (/reader AST, or normalization AST/.test(String(e))) {
cleanCachesAndRetry(); // report upstream if reproducible
} else throw e;
} Prevention
- Clean __generated__ and compiler caches after Relay upgrades
- Keep @updatable usage aligned with current docs
- Minimize and report reproducible compiler panics upstream
When it happens
Trigger: During artifact generation, a grouped operation (in the match arm before this panic) matches no known artifact kind, so neither reader nor normalization AST exists for it.
Common situations: Compiler bug or partially-compiled operation (e.g. an @updatable query missing normalization output) after a Relay upgrade; inconsistent compiler state from an interrupted run.
Related errors
- Unexpected DynamicImport
- Unexpected RelayResolver
- Expected to have a reader operation for `{normal_name}`
- Expected to have a typegen operation for `{normal_name}`
- Expected a key
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/d0c6436f6f1ed198.
Report an issue: GitHub.