Why does my wp_safe_redirect output the ERR_TOO_MANY_REDIRECTS error?

Because you’re in an infinite loop.

When the user visits the site, init fires, and they get redirected to themes.php?page=theme_setup

Then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

So then they arrive on themes.php?page=theme_setup and the init hook fires and redirects them to themes.php?page=theme_setup

…….

The browser, seeing that this keeps happening, has been counting, and when it reaches a threshold, it intervenes, and says NO, you’re looping and it will never end, I’m serving you a warning ( titled ERR_TOO_MANY_REDIRECTS )

So there are a number of problems:

  • UX: Why are you kidnapping the user and sending them there instead of prompting the user to visit themselves? The poor user will be dazed and confused because they expect to be in one place, but have been wrenched from that location and put somewhere else. It’s incredibly confusing. Stop that, it’s not nice
  • Checks and infinite loops: You never check if the user is already on that page, or if a redirect has occurred already. Additionally, if saving that page involves visiting any other page then it won’t save and you’re back to square one
  • Auth: Because it’s on the init hook, it will fire even for logged out users. That code would make the site unviewable as visitors would be redirected to WP Admin which would try to redirect to the login page, triggering your code, leading to more infinite loops
  • Compatibility: Clicking activate is not the only way to activate a theme. E.g. what happens if a user uses the WP.com calypso interface to activate the theme? Or WP CLI? Or clicks live preview instead of activate and does it via the customizer? In all those situations, either a REST API call will be redirected, an AJAX call, or there’s no browser to redirect. A redirection in all this cases will either result in no redirection, or break it entirely
  • Live Preview Issues: Previewing the theme via the customizer would lead the preview pane towards the settings page, making the theme impossible to preview

So, instead, I recommend that you prompt the user by prompting them, not redirecting them. Do this by adding a banner to the admin area like most themes and plugins do, or a modal dialog. Set up good sane defaults too, and don’t do it on the frontend, admin only, and check if the user is logged in and has the ability to set those settings. You don’t want to give a subscriber role the prompt or access to settings.

Redirects like this are bad, hostile, and break things. If you want to prompt the user, just prompt them