A nightly job pushes all my repos. It started failing on the HTTPS ones: could not read Username for 'https://github.com'. From my terminal the same push worked fine.
The credential was in the macOS keychain the whole time. The problem was the helper that reads it. My credential.helper = osxkeychain lived only in Xcode’s bundled gitconfig — which my interactive shell loads, but the launchd cron, running the same /usr/bin/git in a barer environment, does not.
One line fixed it: git config --global credential.helper osxkeychain. Global config gets read no matter who launches git or how stripped their environment is.
The lesson: “it works in my terminal” can quietly mean a config file your shell happens to source. A daemon runs the same binary without your shell’s furniture. Put anything cron depends on in global config.