{"record":{"id":"95faa9ec969ee3df","repo":"ReactiveX/rxjs","slug":"marblecontext-setinterval-callback-must-be-a-func","errorCode":null,"errorMessage":"MarbleContext.setInterval: callback must be a function","messagePattern":"MarbleContext\\.setInterval: callback must be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/rxjs/src/testing/index.ts","lineNumber":90,"sourceCode":"      return new Promise((resolve) => {\n        patched(() => resolve(value!), delay ?? 0);\n      });\n    };\n\n    return patched;\n  })();\n\n  #clearTimeout: typeof globalThis.clearTimeout = (id: Parameters<typeof originalClearTimeout>[0]) => {\n    if (id === undefined) return;\n    this.#removeTimer(+id);\n  };\n\n  #setInterval: typeof globalThis.setInterval = (() => {\n    const patched = (callback: TimerHandler, delay = 0, ...args: any[]): any => {\n      const id = ++this.#timerId;\n      const time = this.#now + delay;\n      if (typeof callback !== 'function') {\n        throw new TypeError('MarbleContext.setInterval: callback must be a function');\n      }\n      const item: TimerQueueItem = { id, callback: callback as (...args: any[]) => void, delay, time, type: 'interval', args };\n      this.#addTimer(item);\n\n      if (this.#shouldUseNodeTimeout) {\n        let ref = false;\n        const nodeTimeout = {\n          ref: () => {\n            ref = true;\n            return nodeTimeout;\n          },\n          unref: () => {\n            ref = false;\n            return nodeTimeout;\n          },\n          hasRef: () => {\n            return ref;\n          },","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/rxjs/src/testing/index.ts#L72-L108","documentation":"The TestScheduler's patched setInterval requires a function callback, matching strict modern timer semantics. Passing a string (old IE-style eval form), null, or undefined throws instead of scheduling an interval.","triggerScenarios":"Inside TestScheduler.run(() => { setInterval('tick()', 1000) }) or setInterval(undefined, 1000) from a forwarding bug in code under test.","commonSituations":"Porting legacy code that used string callbacks with setInterval, or wrappers like myInterval(cb) that pass an uninitialized cb variable.","solutions":["Pass a function reference or arrow function: setInterval(() => tick(), 1000)","Guard forwarding wrappers: if (typeof cb === 'function') setInterval(cb, ms)","Initialize callback variables before use; make callback params required in types"],"exampleFix":"// before\nsetInterval('tick()', 1000);\n// after\nsetInterval(tick, 1000); // or setInterval(() => tick(), 1000)","handlingStrategy":"type-guard","validationCode":"if (typeof callback !== 'function') throw new TypeError('callback must be a function');\nsetInterval(callback, delay);","typeGuard":"const isFn = (cb: unknown): cb is (...args: any[]) => void => typeof cb === 'function';","tryCatchPattern":null,"preventionTips":["Replace string callbacks with arrow functions before marble testing","Initialize callback variables before calling setInterval","Make callback parameters required in wrapper signatures"],"tags":["test-scheduler","marble-testing","setinterval","callback-validation"],"backgroundTag":"invalid-callback-type","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}