You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Books API突发401无效凭证错误排查求助

问题:Google Play Books API上传时返回401 Invalid Credentials错误

原本使用Python脚本将Google Drive中的epub文件上传至Google Play Books库,此前运行完全正常,近期突然失效。执行上传操作时,Google Books API返回401“Invalid Credentials”错误,已尝试重新认证获取新令牌,但问题仍未解决。


OAuth2.0令牌获取脚本

from flask import Flask, request
from google.oauth2.credentials import Credentials
import google_auth_oauthlib.flow
import json
import os


def credentials_to_dict(credentials):
    return {
        "token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": credentials.scopes,
        "expiry": credentials.expiry.isoformat() if credentials.expiry else None,
    }

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    "client_secrets.json", scopes=["https://www.googleapis.com/auth/books"]
)

flow.redirect_uri = "https://callback/callback"

authorization_url, state = flow.authorization_url(
    access_type="offline",
    include_granted_scopes="true",
    prompt="consent",
)

print("Please go to this URL:", authorization_url)


app = Flask(__name__)

@app.route("/callback")
def callback():
    code = request.args.get("code")

    flow.fetch_token(code=code)
    creds = flow.credentials

    cred_dict = credentials_to_dict(creds)

    with open("credentials.json", "w") as outfile:
        json.dump(cred_dict, outfile)

    return "Authentication successful!"

app.run(host="127.0.0.1", port=8000)

credentials.json示例

{
    "token": "ya29.a0AcM612xFMQgQrX-jI1meqxyi2jZrTjiGN0rFiHQWq_Dg9hf7JKJXrtrb5gjCAZieeRxSZ-6k-X4a_qvZBF4rRGW4GQO2ksBkDobvMfbcBmem_zfHS_fN6I83Hf1naCgYKAWYSARESFQHGX2MihDmMJS1g7doDnZTdFV7iKg0175",
    "refresh_token": "1//09DwiJZIARAAGAkSNwF-L9VjT8t8aqhQIZ65Gl1Ga68C03ZB-M7Jz4Z5x3OB-ONj_YPc_pMVU8mI_i9g",
    "token_uri": "https://oauth2.googleapis.com/token",
    "client_id": "726229-jsq22ptgsjv4umdkf0sg0tljc.apps.googleusercontent.com",
    "client_secret": "GOCSPX-CLuI2JI0_n0gU",
    "scopes": [
        "https://www.googleapis.com/auth/books"
    ],
    "expiry": "2024-08-21T16:57:53.932878"
}

上传脚本

from googleapiclient.discovery import build
import google.auth.transport.requests as refresh
from google.oauth2.credentials import Credentials
import sys

id=sys.argv[1]

def upload_epub_to_play_books():
    # Load the OAuth 2.0 credentials from a file
    creds = Credentials.from_authorized_user_file('/home/varo/scripts/playbooks/credentials.json')

    if not creds.valid:
        if creds.refresh_token:
            request = refresh.Request()
            creds.refresh(request)

        if creds.valid:
            print("Creds valid and were refreshed")
            with open('/home/varo/scripts/playbooks/credentials.json', 'w') as token:
                token.write(creds.to_json())
    else:
        print("creds already valid")

    # Create a Google Play Books API service client
    service = build('books', 'v1', credentials=creds)

    # Upload the epub file to Google Play Books
    response = service.cloudloading().addBook(drive_document_id=id).execute()
    print(response)


# Upload an epub file to Google Play Books
upload_epub_to_play_books()

报错信息

This is the id: 1gODrE4DeRzmiwjrE3XTc-J8tToJ7Lh2F
Creds valid and were refreshed
Traceback (most recent call last):
  File "/home/varo/scripts/playbooks/add_to_playbooks.py", line 33, in <module>
    upload_epub_to_play_books()
  File "/home/varo/scripts/playbooks/add_to_playbooks.py", line 28, in upload_epub_to_play_books
    response = service.cloudloading().addBook(drive_document_id=id).execute()
  File "/home/varo/.local/lib/python3.9/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/home/varo/.local/lib/python3.9/site-packages/googleapiclient/http.py", line 938, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 401 when requesting https://books.googleapis.com/books/v1/cloudloading/addBook?drive_document_id=1gODrE4DeRzmiwjrE3XTc-J8tToJ7Lh2F&alt=json returned "Invalid Credentials". Details: "[{'message': 'Invalid Credentials', 'domain': 'global', 'reason': 'authError', 'location': 'Authorization', 'locationType': 'header'}]">

排查建议
  1. 修正OAuth回调地址匹配问题
    脚本中设置的redirect_uri为https://callback/callback,但本地Flask服务实际回调地址是http://127.0.0.1:8000/callback,两者完全不匹配。需在Google Cloud控制台的OAuth客户端配置中,将回调地址改为http://127.0.0.1:8000/callback,同时同步修改脚本中的flow.redirect_uri值,确保三者一致。

  2. 统一令牌序列化逻辑
    认证脚本用自定义的credentials_to_dict序列化令牌,上传脚本用官方的creds.to_json()写入文件,两种格式可能存在差异。建议修改认证脚本,直接用creds.to_json()生成文件内容,避免手动序列化导致的格式错误:

    # 替换原认证脚本中的写入逻辑
    with open("credentials.json", "w") as outfile:
        outfile.write(creds.to_json())
    
  3. 验证API启用状态与权限范围

    • 登录Google Cloud控制台,确认对应项目已启用Google Play Books API;
    • 重新认证时,确认授权页面明确显示「管理您的Google Play Books库」权限,确保令牌包含https://www.googleapis.com/auth/books范围。
  4. 更新依赖库版本
    旧版本的Google认证库可能存在令牌刷新bug,执行以下命令更新到最新稳定版:

    pip install --upgrade google-auth google-auth-oauthlib google-api-python-client flask
    
  5. 检查Drive文件权限
    确认OAuth授权的账号有权限访问指定的Drive文件(drive_document_id对应文件),若文件为私有且未共享给该账号,也可能触发权限类错误。

内容的提问来源于stack exchange,提问作者Varo OP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:23:15