Redux: Question: "Reducers may not dispatch actions"

Created on 8 Dec 2017  路  16Comments  路  Source: reduxjs/redux

I haven't been able to replicate this in an isolated environment, but it's an error which keeps getting picked up from our error reporter.

We have two websockets which asynchronously push data to the client. Upon receiving messages on these websockets, redux store actions are dispatched.

What appears to be happening based on our error stack trace is that mid-way between one state update cycle occurring (perhaps in between reducer function calls) an asynchronous message is being received on one of the sockets, and this somehow is prioritised above the current redux state update cycle - thus when it gets to the end of the cycle it sees that an action has been dispatched in that time, and throws the error.

Stack trace from error capture:
image

As you can see there are two calls to "src/api/asyncListeners -> listener" - these are both reactions to asynchronous events, and are not related / triggered by each other.

Below is an example fiddle which doesn't show the problem. Clearly this code waits for the first state update cycle to run before processing the next action:
https://jsfiddle.net/pojzf7vx/2/

TL;DR;

  • When the "Reducers may not dispatch actions" error is thrown, is this because something is likely to have gone wrong in the redux store? Or is this a best practice warning to prevent, say, infinite loops?

  • Will redux start a secondary state update cycle if it receives an action mid-update?

  • Is there any way to force redux to process full state updates for actions one at a time?

Appreciate any help/advice!

All 16 comments

Is one of your reducers asynchronous in some way? If so, that is considered a side effect. You should push any side effects to be handled in your middleware, not your reducer functions. They should be pure (no global variable access, including setTimeout).

@timdorr the reducers are all pure synchronous functions

@timdorr This question seems to have been closed prematurely.

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux where there are a lot more people ready to help you out. Thanks!

@cjpete interestingly I just found the same exact error pop up in my sentry logs. I use redux-saga to manage all asynchronous control flows. In my case I too have a websocket that is dispatching an action. Later I came to a similar conclusion that it must somehow be prioritizing the newly dispatched action from the websocket push over the currentReducer.

The reducers are small, straightforward, and include no asynchronous code in them such as setTimeout or requestAnimationFrame. This is an extremely rare occurrence, and even though this code has been running in production for 6 months by a massive number of users, we've only received this error the one time for a single user.

They were using Chrome 63.0.3239 on Windows 8.1. If I figure out a way to reproduce it I will surely post back here. But I thought you'd be happy to know you are not the only one.

Also started seeing some of these errors show up in sentry (IE11, Windows).

We have a custom promise based request middleware w/ redux-thunk, all reducers are pure functions. I wonder if there is any connection with https://github.com/gaearon/redux-thunk/issues/122.

This admittedly _seems_ to be "impossible". I would really appreciate it if someone could manage to come up with a repro of some kind.

I'm also seeing this Reducers may not dispatch actions error pop up in Sentry logs, and I'm also not (yet) able to reproduce it. However, for what it's worth, in my case exactly 100% of these errors are coming from some version of Internet Explorer, mostly IE 11.

it's happening to me too (in react native), and it's crashing the app... and likewise, none of my reducers are dispatching actions, and all are synchronous

"dependencies": {
    "react": "16.2.0",
    "react-native": "0.52",
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
    "redux-thunk": "^2.2.0"
  }

some news about this ? I have the same problem.

export function fetchDashboard() {
  return (dispatch) => {
    dispatch(isFetching()); // Error
  };
}
"react": "16.2.0",
"react-native": "0.52.0",
"react-redux": "5.0.5",
"redux": "3.7.2",
"redux-thunk": "2.2.0"

For now this is the solution:
https://github.com/reduxjs/redux-thunk/issues/122

I agree this should be impossible. However I managed to recreate this using Chrome 75.0.3770.100. I'm trying to get this to happen again but I haven't managed to get it to happen again. But I would be interested if someone else can get it to happen again. I burrowed the prime number functions from https://www.npmjs.com/package/prime-number

/**
 * Benchmark a primality algorythm
 *
 * @param {Function} isPrime
 *
 * @returns {Function} checkPrimality (from, to)
 */
function primeBenchMark(isPrime) {
  /**
   * Primality check on given range
   *
   * @param {Number} from
   * @param {Number} to
   */
  return function checkPrimality(from, to) {
    console.time('primality benchmark');

    var countPrimes = 0;

    for (var i = from; i <= to; i++) if (isPrime(i)) countPrimes++;

    console.log(`Found ${countPrimes} primes`);

    console.timeEnd('primality benchmark');
  };
}

/**
 * Check if a number is prime
 *
 * @param {Number} n
 *
 * @returns {Boolean} isPrime
 */
function primeNumber(n) {
  if (n === 2) return true; // 2 is a special case
  if (n % 2 === 0) return false;
  for (var i = 3; i <= Math.sqrt(n); i = i + 2) {
    if (!primeNumber(i)) continue; // <-- recursion here
    if (n % i === 0) return false;
  }
  return true;
}

let lock = false;
let count = 0;

/**
 * A function that checks javascript is single threaded, by setting a lock, and computing a prime numbers, given js is
 * single threaded each iteration should finish executing before the next one begins
 *
 * @return {void}
 */
function lockedFunction() {
  count++;
  console.info(`Started ${count}`);

  if (lock) {
    throw new Error(`Not single threaded ${lock} hasn't finished executing before ${count} executed`);
  }

  try {
    lock = count;
    primeBenchMark(primeNumber)(10000, 500000);
  } finally {
    lock = false;
  }

  console.info(`Ended ${count}`);
};


/**
 * Runs a given function many times
 *
 * @param {function} func the function to run
 * @param {number} times to run the calculation
 * @param {number} delay ms delay time between executions
 *
 * @return {void}
 */
function runMany(func, times, delay) {
  let count = 0;

  function runCycle() {
    func();
    if (count < times) {
      count++;
      setTimeout(runCycle, delay);
    }
  };

  runCycle();
};

/**
 * Runs a function that computes prime numbers many times
 *
 * @param {number} times to run the calculation
 * @param {number} delay ms delay time between executions
 *
 * @return {void}
 */
function lockTest(times = 100, delay = 50) {
  runMany(lockedFunction, times, delay);
};

Running lockTest(); a few times in the console I got this to finally happen

Started 325
index.js?m=1562193618:155900 Started 326
index.js?m=1562193618:155900 Uncaught Error: Not single threaded 325 hasn't finished executing before 326 executed
    at lockedFunction (index.js?m=1562193618:155900)
    at runCycle (index.js?m=1562193618:155907)
lockedFunction @ index.js?m=1562193618:155900
runCycle @ index.js?m=1562193618:155907
setTimeout (async)
runCycle @ index.js?m=1562193618:155907
setTimeout (async)
runCycle @ index.js?m=1562193618:155907
setTimeout (async)
runCycle @ index.js?m=1562193618:155907
setTimeout (async)
runCycle @ index.js?m=1562193618:155907
runMany @ index.js?m=1562193618:155907
lockTest @ index.js?m=1562193618:155914
(anonymous) @ VM293424:1
index.js?m=1562193618:155900 Started 327

But this has been driving me nuts. If the redux store ever get into this state it cannot recover. Contemplating forking off my own version to unset isDispatching when this error occurs, which I don't think we should have to do but it's better then the app breaking because of it.

To be blunt: if there's an issue here, it's not the fault of Redux. The logic is sound - there's nothing we can do about environments that might be somehow doing something bizarre.

I know that, firefox is fine as far as I have tested it, and this should be impossible with javascript running as a single thread. But letting people know should they randomly experience this issue, as I can't find much about this issue online. Also confirming that this is not a redux issue, but this issue will break it when it occurs.

Yea, I mean this issue has been closed since it was created. I never really expected anything to be done about this. I don鈥檛 believe the issue is in the redux source or our source at all.

It is good to collect what information we do have on the subject to see if we could find the true cause, but it is a difficult one.

In the case above, if you are able to reproduce a problem with JS execution order perhaps this discussion should be moved to a ticket here?

Perhaps they will tell us this is a possibility due to some extreme circumstance we haven鈥檛 thought up.

Yeah my search still goes on to isolate the exact cause, it is really driving me nuts. Thinking about this some more, it seems the current execution just completely stops silently, and so it never progresses to the finally statement, if it did it would eventually recover, but it does not.

Thanks for the link, I'll see if anyone has reported this issue, if not I'll continue trying to isolate it to reproducible steps and report it myself.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

timdorr picture timdorr  路  56Comments

5h1rU picture 5h1rU  路  115Comments

markerikson picture markerikson  路  108Comments

nickknw picture nickknw  路  71Comments

gaearon picture gaearon  路  61Comments