facebook/relay · error
RelayObservable: Expected pollInterval to be positive, got:
Error message
RelayObservable: Expected pollInterval to be positive, got: ${pollInterval} What it means
`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.
Source
Thrown at packages/relay-runtime/network/RelayObservable.js:417
});
return () => {
subscriptions.forEach(sub => sub.unsubscribe());
subscriptions.length = 0;
};
});
}
/**
* Returns a new Observable which first mirrors this Observable, then when it
* completes, waits for `pollInterval` milliseconds before re-subscribing to
* this Observable again, looping in this manner until unsubscribed.
*
* The returned Observable never completes.
*/
poll(pollInterval: number): RelayObservable<T> {
if (__DEV__) {
if (typeof pollInterval !== 'number' || pollInterval <= 0) {
throw new Error(
'RelayObservable: Expected pollInterval to be positive, got: ' +
pollInterval,
);
}
}
return RelayObservable.create(sink => {
let subscription;
let timeout;
const poll = () => {
subscription = this.subscribe({
next: sink.next,
error: sink.error,
complete() {
timeout = setTimeout(poll, pollInterval);
},
});
};View on GitHub (pinned to 668b1b85e0)
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
Example fix
// before const interval = Number(config.pollSeconds); // 0 or NaN observable.poll(interval); // after const interval = (Number(config.pollSeconds) || 30) * 1000; observable.poll(interval);
Defensive patterns
Strategy: validation
Validate before calling
function safePoll(obs, intervalMs) {
if (typeof intervalMs !== 'number' || !(intervalMs > 0)) {
throw new TypeError(`pollInterval must be a positive number, got: ${intervalMs}`);
}
return obs.poll(intervalMs);
} Type guard
function isValidPollInterval(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v) && v > 0;
} Try / catch
try {
observable.poll(interval);
} catch (e) {
if (e.message.includes('pollInterval to be positive')) {
observable.poll(DEFAULT_POLL_INTERVAL_MS);
} else throw e;
} Prevention
- 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
When it happens
Trigger: `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.
Common situations: 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).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- BabelPluginRelay: Expected plugin context to include "types"
- Babel state is missing expected file name
- Source must be a Function: ${String(source)}
- Observer must be an Object with callbacks: ${String(observer
- Returned cleanup function which cannot be called: ${String(c
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/9211914cf35e4414.
Report an issue: GitHub.