facebook/relay · error
Source must be a Function: ${String(source)}
Error message
Source must be a Function: ${String(source)} What it means
RelayObservable's constructor requires its `source` argument to be a function (the source's start callback). This dev-mode check throws immediately when you construct RelayObservable directly with a non-function (or falsy) source. The public API is `RelayObservable.create(source)`.
Source
Thrown at packages/relay-runtime/network/RelayObservable.js:104
* synchronously, avoiding any UI jitter, while providing a compositional API,
* which simplifies logic and prevents mishandling of errors compared to
* the direct use of callback functions.
*
* ESObservable: https://github.com/tc39/proposal-observable
*/
class RelayObservable<out T> implements Subscribable<T> {
readonly _source: Source<T>;
static create<V>(source: Source<V>): RelayObservable<V> {
return new RelayObservable(source as any);
}
// Use RelayObservable.create()
constructor(source: empty): void {
if (__DEV__) {
// Early runtime errors for ill-formed sources.
if (!source || typeof source !== 'function') {
throw new Error('Source must be a Function: ' + String(source));
}
}
(this as any)._source = source;
}
/**
* When an emitted error event is not handled by an Observer, it is reported
* to the host environment (what the ESObservable spec refers to as
* "HostReportErrors()").
*
* The default implementation in development rethrows thrown errors, and
* logs emitted error events to the console, while in production does nothing
* (swallowing unhandled errors).
*
* Called during application initialization, this method allows
* application-specific handling of unhandled errors. Allowing, for example,
* integration with error logging or developer tools.
*View on GitHub (pinned to 668b1b85e0)
Solutions
- Use the static factory: `RelayObservable.create(sourceFunction)` instead of `new RelayObservable(...)`
- Wrap the value: for a promise use `create(env => promise.then(...))` or convert with an existing adapter (`fromPromise`, `fromAsyncIterable`)
- Ensure the variable passed is actually the source function and not the result of invoking it
Example fix
// before
new RelayObservable(promise);
// after
RelayObservable.create(sink => {
promise.then(v => sink.next(v), e => sink.error(e));
}); Defensive patterns
Strategy: validation
Validate before calling
function safeCreateObservable(source) {
if (typeof source !== 'function') {
throw new TypeError('RelayObservable.create requires a source function');
}
return RelayObservable.create(source);
} Type guard
function isObservableSource(source: unknown): source is (sink: Sink<unknown>) => void | (() => void) | {unsubscribe: () => void} {
return typeof source === 'function';
} Try / catch
try {
const obs = RelayObservable.create(source);
} catch (e) {
if (e.message.startsWith('Source must be a Function')) {
throw new TypeError('Pass a source function to RelayObservable.create, not ' + typeof source, {cause: e});
}
throw e;
} Prevention
- Always use RelayObservable.create(fn), never new RelayObservable(...)
- Wrap promises with fromPromise or a create(env => ...) callback instead of passing them directly
- Check you're not passing the invoked result (fn()) instead of fn
- Remember this check only fires in __DEV__ builds — production may fail more subtly
When it happens
Trigger: `new RelayObservable(notAFunction)` — passing an object, promise, array, or undefined/null directly to the constructor; calling `create` with a non-function value in __DEV__ builds.
Common situations: Porting code from other Observable libs (RxJS accepts objects/arrays as sources); forgetting to wrap a promise in a function; typos passing the wrong variable to the constructor.
Related errors
- Observer must be an Object with callbacks: ${String(observer
- Returned cleanup function which cannot be called: ${String(c
- RelayObservable: Expected pollInterval to be positive, got:
- BabelPluginRelay: Expected plugin context to include "types"
- BabelPluginRelay: Expected exactly one definition per graphq
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/de34d0c0444d8e87.
Report an issue: GitHub.