{"record":{"id":"9211914cf35e4414","repo":"facebook/relay","slug":"relayobservable-expected-pollinterval-to-be-posit","errorCode":null,"errorMessage":"RelayObservable: Expected pollInterval to be positive, got: ${pollInterval}","messagePattern":"RelayObservable: Expected pollInterval to be positive, got: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/relay-runtime/network/RelayObservable.js","lineNumber":417,"sourceCode":"      });\n\n      return () => {\n        subscriptions.forEach(sub => sub.unsubscribe());\n        subscriptions.length = 0;\n      };\n    });\n  }\n\n  /**\n   * Returns a new Observable which first mirrors this Observable, then when it\n   * completes, waits for `pollInterval` milliseconds before re-subscribing to\n   * this Observable again, looping in this manner until unsubscribed.\n   *\n   * The returned Observable never completes.\n   */\n  poll(pollInterval: number): RelayObservable<T> {\n    if (__DEV__) {\n      if (typeof pollInterval !== 'number' || pollInterval <= 0) {\n        throw new Error(\n          'RelayObservable: Expected pollInterval to be positive, got: ' +\n            pollInterval,\n        );\n      }\n    }\n    return RelayObservable.create(sink => {\n      let subscription;\n      let timeout;\n      const poll = () => {\n        subscription = this.subscribe({\n          next: sink.next,\n          error: sink.error,\n          complete() {\n            timeout = setTimeout(poll, pollInterval);\n          },\n        });\n      };","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/packages/relay-runtime/network/RelayObservable.js#L399-L435","documentation":"`RelayObservable.prototype.poll(interval)` requires a strictly positive numeric millisecond interval. This dev-mode check throws when the interval is not a number or is <= 0, because polling with a non-positive interval would loop without any delay and starve the event loop.","triggerScenarios":"`source.poll(0)`, `source.poll(-1000)`, or `source.poll(undefined/null/NaN)` — typically when the interval comes from a config value or env var parsed incorrectly.","commonSituations":"Config defaulting to 0 for \"no polling\"; `Number(process.env.POLL_MS)` producing NaN; unit confusion (seconds vs milliseconds, e.g. passing 0.5 seconds instead of 500).","solutions":["Pass a positive number in milliseconds: `poll(1000)`","Guard config values: `pollInterval > 0 ? pollInterval : DEFAULT_INTERVAL`","Fix env parsing with a fallback: `Number(process.env.POLL_MS) || 30000`","If you don't want polling, don't call `poll` at all — use `create` or `fromPromise` instead"],"exampleFix":"// before\nconst interval = Number(config.pollSeconds); // 0 or NaN\nobservable.poll(interval);\n// after\nconst interval = (Number(config.pollSeconds) || 30) * 1000;\nobservable.poll(interval);","handlingStrategy":"validation","validationCode":"function safePoll(obs, intervalMs) {\n  if (typeof intervalMs !== 'number' || !(intervalMs > 0)) {\n    throw new TypeError(`pollInterval must be a positive number, got: ${intervalMs}`);\n  }\n  return obs.poll(intervalMs);\n}","typeGuard":"function isValidPollInterval(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v) && v > 0;\n}","tryCatchPattern":"try {\n  observable.poll(interval);\n} catch (e) {\n  if (e.message.includes('pollInterval to be positive')) {\n    observable.poll(DEFAULT_POLL_INTERVAL_MS);\n  } else throw e;\n}","preventionTips":["Validate config/env-derived intervals: Number(x) || DEFAULT, then check > 0","Remember the unit is milliseconds; convert seconds explicitly","Don't use 0 to mean \"disabled\" — skip calling poll entirely","Use Number.isFinite checks to catch NaN from parsing"],"tags":["relay","observable","polling","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}