The trigger for this was an issue we've had at work, where on a D9 site the developers had accidentally removed user.settings.yml completely. The result of this was that we could not log into the site with drush uli - we'd always get a "You have tried to use a reset password link that has expired..." error (otherwise the site was still fully operational). It took us some ridiculous amount of time to figure out what the actual culprit was, so once we did, one of our colleagues has raised https://www.drupal.org/project/drupal/issues/3258523 for Drupal core.
We have similar code in Backdrop:
// Time out, in seconds, until login URL expires.
$timeout = config_get('system.core', 'user_password_reset_timeout');
...
// No time out for first time login.
if ($account->login && $current - $timestamp > $timeout) {
backdrop_set_message(t('You have tried to use a reset password link that has expired. Please request a new one using the form below.'), 'error');
backdrop_goto('user/password');
}
The difference in Backdrop is that the value for user_password_reset_timeout is saved in system.core.json, so if you try to delete this file, the entire site blows up on you:

So I tried simulating this by removing the user_password_reset_timeout entry from system.core.json instead, and I indeed got myself into the same situation as in Drupal (not being able to log into the site via drush uli, or via password reset links sent through email):

One could argue the following:
- It is unlikely that people will go deleting entries from config files manually.
- Instead of blindly relying on config_get() returning a proper value for the variable you are using, make sure that a default is specified in-code (do some defensive coding). In other words (using this specific use case as an example - the problem is more broad), this nasty situation could easily be avoided if we did something like this:
$timeout = config_get('system.core', 'user_password_reset_timeout') !== NULL ? config_get('system.core', 'user_password_reset_timeout') : 86400;
These are all valid remarks, but how about if we could be able to do this instead?:
$timeout = config_get('system.core', 'user_password_reset_timeout', 86400);
In other words, get from this: config_get($config_file, $option = NULL)
...to this: config_get($config_file, $option = NULL, $default = NULL)
Other thoughts around this: - We should be more helpful/specific in the error we throw when crucial config files are missing. Listing these files in the error we throw would help people troubleshoot things quicker - separate issue though. - We could introduce a "debugging mode" were warnings would be logged in dblog when trying to read a config file/value that is missing from the file system (don't want to have this on by default - it could possibly flood the dblog) - also separate issue.
Recent comments
As I work through things I flush the caches after every change I make. As far as I can tell I have the custom module in the correct place (/modules/custom/fix_zero_search) and the file...
custom module not showing up in modules list
If you haven't already, flush all the caches. If it is in the modules folder whether directly or in a custom folder it will show up. If not, something else is wrong with your site; check...
custom module not showing up in modules list
Awful idea! That's not how modules get installed! The number in db indicates the installed state and schema version. You're not supposed to fiddle in the database directly. There's an...
custom module not showing up in modules list