Cron执行Google Drive备份脚本时出现Invalid grant错误排查
太懂这种手动跑没问题、定时任务就掉链子的糟心了!结合你遇到的invalid_grant错误,核心问题基本出在Cron执行环境和手动SSH会话的差异上,下面给你逐个排查解决:
1. 先排查脚本的工作目录问题
Cron默认是在你的用户主目录(/home/myuser)下执行脚本的,而你手动运行时大概率是在脚本所在的目录里操作。如果你的备份脚本里用了相对路径来存储OAuth令牌(比如./token.json或者credentials/),那Cron执行时会去/home/myuser下面找这些文件,而不是脚本所在的目录,自然读不到有效的刷新令牌,只能重新请求授权——但你的authCode可能已经被用过或者过期了,就会抛出invalid_grant。
解决办法:
- 在脚本里把所有相对路径改成绝对路径,比如把令牌存储路径从
./token.json改成/var/www/backupgoogledrive/token.json(换成你实际的脚本目录)。 - 或者在Cron任务里先切换到脚本目录再执行,比如把Cron命令改成:
5 0 * * 6 cd /path/to/backupgoogledrive && php backup_script.php
2. 检查Cron的环境变量配置
手动SSH会话里有完整的环境变量(比如PATH、PHP的环境变量、甚至Google API相关的配置),但Cron的环境变量非常精简,可能连正确的PHP路径都找不到,或者缺少脚本依赖的环境变量,导致OAuth请求失败。
解决办法:
- 在Cron任务里指定完整的PHP路径,比如你手动执行时用的是
/usr/bin/php,那Cron命令里就写全:5 0 * * 6 cd /path/to/backupgoogledrive && /usr/bin/php backup_script.php - 或者把手动会话的环境变量导入到Cron里:先在SSH里执行
env > ~/cron_env.txt,然后在Cron任务开头加上source ~/cron_env.txt &&,比如:5 0 * * 6 source /home/myuser/cron_env.txt && cd /path/to/backupgoogledrive && php backup_script.php
3. 确认OAuth令牌的权限和存储路径
手动执行时生成的OAuth令牌文件(比如token.json),可能属于当前SSH会话的用户权限,但Cron执行时虽然是同一个用户,可能因为umask或者目录权限的问题,导致无法读取或写入令牌文件。如果令牌文件读不到,脚本就会尝试用authCode重新获取令牌,而authCode只能用一次,重复使用就会触发invalid_grant。
解决办法:
- 找到令牌文件的存储位置,给它设置正确的权限:
chmod 600 /path/to/backupgoogledrive/token.json chown myuser:myuser /path/to/backupgoogledrive/token.json - 确保脚本里的令牌路径是绝对路径,避免Cron找错地方。
4. 检查服务器时间同步
Google的OAuth服务对时间精度要求很高,如果你的服务器系统时间和真实UTC时间偏差超过5分钟,就会导致令牌验证失败,抛出invalid_grant。手动执行时你可能没注意这个问题,但Cron执行时这个时间差就会触发错误。
解决办法:
- 同步服务器时间,比如用
timedatectl或者ntpdate:# 适合systemd系统的同步方式 sudo timedatectl set-ntp true # 或者用ntpdate同步 sudo ntpdate pool.ntp.org - 执行完后用
date命令检查时间是否和真实时间一致。
最后验证Cron任务
修改完后,你可以先设置一个每分钟执行的Cron任务快速测试,比如:
* * * * * cd /path/to/backupgoogledrive && /usr/bin/php backup_script.php >> /home/myuser/backup_cron_log.txt 2>&1
然后查看backup_cron_log.txt和error_log,确认是否还有错误。
内容的提问来源于stack exchange,提问作者user20152015

