facebook/relay · error

Unexpected JSModuleDependency

Error message

Unexpected JSModuleDependency

What it means

Primitive::JSModuleDependency represents a required JS module import (e.g. from @match or client extensions) and is emitted via module import statements, never as a constant value. write_constant_value panics if such a dependency appears in an argument or embedded literal position, since there is no valid JS literal for a module reference in that context.

Source

Thrown at compiler/crates/relay-codegen/src/printer.rs:933

                        write_constant_value(f, builder, value)?;
                        f.push(',');
                    }
                    if !obj.is_empty() {
                        f.pop();
                    }
                    f.push('}');
                    Ok(())
                }
            }
        }
        Primitive::Null | Primitive::SkippableNull => {
            f.push_str("null");
            Ok(())
        }
        Primitive::StorageKey(_, _) => panic!("Unexpected StorageKey"),
        Primitive::RawString(_) => panic!("Unexpected RawString"),
        Primitive::GraphQLModuleDependency(_) => panic!("Unexpected GraphQLModuleDependency"),
        Primitive::JSModuleDependency { .. } => panic!("Unexpected JSModuleDependency"),
        Primitive::ResolverModuleReference { .. } => panic!("Unexpected ResolverModuleReference"),
        Primitive::PropertyAccessor(_) => panic!("Unexpected PropertyAccessor"),
        Primitive::DynamicImport { .. } => panic!("Unexpected DynamicImport"),
        Primitive::RelayResolverModel { .. } => panic!("Unexpected RelayResolver"),
    }
}

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure module dependencies are only produced in module/import positions (write_module_import path), not in arguments
  2. Restructure the document so @match/@module usage keeps dependencies in selections rather than arguments
  3. Fix custom transforms to use the proper import mechanism instead of embedding dependencies in values
  4. Update the Relay compiler to a version whose IR/printer contract matches your pipeline

Example fix

// before
argument: <JSModuleDependency("./Avatar_user.react")> // inside an argument value
// after
argument: "Avatar_user" // plain string literal; import emitted via module path
Defensive patterns

Strategy: validation

Validate before calling

function assertNoJsDepsInArgs(doc) {
  visit(doc, {
    Argument(node) {
      if (node.value.kind !== 'Variable' && !isLiteralValue(node.value)) {
        throw new Error(`Argument ${node.name.value} must be a literal value`);
      }
    }
  });
}

Type guard

const isJsDep = (p) => p != null && p.type === 'jsModuleDependency';

Try / catch

try {
  generateArtifacts();
} catch (e) {
  if (String(e).includes('Unexpected JSModuleDependency')) {
    // route module imports through the import path, not arguments
  }
  throw e;
}

Prevention

When it happens

Trigger: A JSModuleDependency primitive produced by the builder for @match/client-extension handling reaching an argument value or constant position during printing; custom transforms inserting JS module dependencies into argument values.

Common situations: Using @match/@module features where the dependency ends up in an argument position instead of a selection; custom codegen passes constructing JSModuleDependency Primitives in the wrong slot; compiler upgrade changing where dependencies are emitted.

Related errors


AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02). Data as JSON: /api/errors/d1980058b7dd0da4. Report an issue: GitHub.