{"record":{"id":"de34d0c0444d8e87","repo":"facebook/relay","slug":"source-must-be-a-function-string-source","errorCode":null,"errorMessage":"Source must be a Function: ${String(source)}","messagePattern":"Source must be a Function: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/relay-runtime/network/RelayObservable.js","lineNumber":104,"sourceCode":" * synchronously, avoiding any UI jitter, while providing a compositional API,\n * which simplifies logic and prevents mishandling of errors compared to\n * the direct use of callback functions.\n *\n * ESObservable: https://github.com/tc39/proposal-observable\n */\nclass RelayObservable<out T> implements Subscribable<T> {\n  readonly _source: Source<T>;\n\n  static create<V>(source: Source<V>): RelayObservable<V> {\n    return new RelayObservable(source as any);\n  }\n\n  // Use RelayObservable.create()\n  constructor(source: empty): void {\n    if (__DEV__) {\n      // Early runtime errors for ill-formed sources.\n      if (!source || typeof source !== 'function') {\n        throw new Error('Source must be a Function: ' + String(source));\n      }\n    }\n    (this as any)._source = source;\n  }\n\n  /**\n   * When an emitted error event is not handled by an Observer, it is reported\n   * to the host environment (what the ESObservable spec refers to as\n   * \"HostReportErrors()\").\n   *\n   * The default implementation in development rethrows thrown errors, and\n   * logs emitted error events to the console, while in production does nothing\n   * (swallowing unhandled errors).\n   *\n   * Called during application initialization, this method allows\n   * application-specific handling of unhandled errors. Allowing, for example,\n   * integration with error logging or developer tools.\n   *","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/packages/relay-runtime/network/RelayObservable.js#L86-L122","documentation":"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)`.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"// before\nnew RelayObservable(promise);\n// after\nRelayObservable.create(sink => {\n  promise.then(v => sink.next(v), e => sink.error(e));\n});","handlingStrategy":"validation","validationCode":"function safeCreateObservable(source) {\n  if (typeof source !== 'function') {\n    throw new TypeError('RelayObservable.create requires a source function');\n  }\n  return RelayObservable.create(source);\n}","typeGuard":"function isObservableSource(source: unknown): source is (sink: Sink<unknown>) => void | (() => void) | {unsubscribe: () => void} {\n  return typeof source === 'function';\n}","tryCatchPattern":"try {\n  const obs = RelayObservable.create(source);\n} catch (e) {\n  if (e.message.startsWith('Source must be a Function')) {\n    throw new TypeError('Pass a source function to RelayObservable.create, not ' + typeof source, {cause: e});\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["relay","observable","dev-mode"],"backgroundTag":"invalid-observable-source","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}