function getInfiniteLoopError(clock, job) {
				const infiniteLoopError = new Error(
					`Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!`
				);

				if (!job.error) {
					return infiniteLoopError;
				}

				// pattern never matched in Node
				const computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/;
				let clockMethodPattern = new RegExp(
					String(Object.keys(clock).join("|"))
				);

				if (addTimerReturnsObject) {
					// node.js environment
					clockMethodPattern = new RegExp(
						`\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+`
					);
				}

				let matchedLineIndex = -1;
				job.error.stack.split("\n").some(function (line, i) {
					// If we've matched a computed target line (e.g. setTimeout) then we
					// don't need to look any further. Return true to stop iterating.
					const matchedComputedTarget = line.match(computedTargetPattern);
					/* istanbul ignore if */
					if (matchedComputedTarget) {
						matchedLineIndex = i;
						return true;
					}

					// If we've matched a clock method line, then there may still be
					// others further down the trace. Return false to keep iterating.
					const matchedClockMethod = line.match(clockMethodPattern);
					if (matchedClockMethod) {
						matchedLineIndex = i;
						return false;
					}

					// If we haven't matched anything on this line, but we matched
					// previously and set the matched line index, then we can stop.
					// If we haven't matched previously, then we should keep iterating.
					return matchedLineIndex >= 0;
				});

				const stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${
					job.func.name || "anonymous"
				}\n${job.error.stack
					.split("\n")
					.slice(matchedLineIndex + 1)
					.join("\n")}`;

				try {
					Object.defineProperty(infiniteLoopError, "stack", {
						value: stack,
					});
				} catch (e) {
					// noop
				}

				return infiniteLoopError;
			}
@param {Clock} clock
@param {Timer} job