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

第二次获取Spotify Refresh Token时出现invalid_grant错误的问题排查

问题分析与解决方案

你的核心问题出在刷新令牌的更新逻辑错误,以及对Spotify令牌机制的理解偏差,下面一步步拆解:

1. 错误根源:误用Access Token作为Refresh Token

看你的get_refresh_token方法里的这行代码:

self.current_refresh_token = token_request_json["access_token"]

这完全搞错了令牌的作用!Spotify的refresh_token是用来获取新access_token的长期凭证,而access_token是短期的API请求凭证。第一次刷新令牌时,Spotify的响应里不会返回新的refresh_token(除非应用权限或配置有特殊要求),所以你不能把返回的access_token赋值给current_refresh_token——这直接导致下一次刷新时,你用无效的access_token当作refresh_token去请求,自然触发invalid_grant错误。

2. 正确的Refresh Token处理逻辑

Spotify的refresh_token默认是长期有效的(只要用户没有撤销你的应用权限),所以你只需要在首次获取令牌时保存好初始的refresh_token,后续刷新时复用这个refresh_token即可。只有当刷新响应里明确返回了新的refresh_token字段时,才需要替换原有值。

3. 代码修正

修复get_refresh_token方法

把错误的赋值语句调整为仅在响应包含新refresh_token时更新:

def get_refresh_token(self):
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = {
        "client_id": "<client-id>",
        "client_secret": "<client-secret>",
        "refresh_token": self.current_refresh_token,
        "grant_type": "refresh_token"
    }
    token_request = requests.post(
        url="https://accounts.spotify.com/api/token",
        data=data,
        headers=headers
    )
    if token_request.status_code == 200:
        token_request_json = json.loads(token_request.text)
        print(token_request_json)
        # 仅当响应返回新的refresh_token时才更新
        if "refresh_token" in token_request_json:
            self.current_refresh_token = token_request_json["refresh_token"]
        return token_request_json
    else:
        print(token_request.status_code)
        print(token_request.text)
        return None  # 返回None避免后续代码报错

优化循环中的令牌刷新逻辑

你的代码每次循环都刷新令牌,这完全没必要——access_token的有效期是3600秒,建议在令牌即将过期时再刷新:

def get_tracks():
    token_manager = TokenManager()
    token = token_manager.get_first_spotify_token()
    if not token:
        print("Failed to get initial token")
        return
    
    limit = 50
    offset = 0
    next_url = ""
    total = 1000000
    # 记录令牌过期时间(当前时间+有效期)
    token_expires_at = time.time() + token["expires_in"]
    
    while offset <= total or next_url is not None:
        # 提前60秒刷新令牌,避免请求时令牌过期
        if time.time() >= token_expires_at - 60:
            new_token = token_manager.get_refresh_token()
            if new_token:
                token = new_token
                token_expires_at = time.time() + token["expires_in"]
            else:
                print("Failed to refresh token")
                break
        
        params = {
            "limit": limit,
            "offset": offset
        }
        headers = {
            "Authorization": f"{token['token_type']} {token['access_token']}"
        }
        tracks_request = requests.get(
            url="https://api.spotify.com/v1/me/tracks",
            headers=headers,
            params=params
        )
        
        if tracks_request.status_code != 200:
            print(f"Track request failed: {tracks_request.status_code} - {tracks_request.text}")
            break
            
        tracks_json_response = tracks_request.json()  # 用requests内置json方法更简洁
        offset += limit
        total = tracks_json_response["total"]
        next_url = tracks_json_response["next"]
        
        # 这里可以添加曲目数据处理逻辑,比如打印或保存
        # for item in tracks_json_response["items"]:
        #     print(item["track"]["name"])

4. 额外注意事项

  • 确保你的授权请求包含offline_access scope,这是获取refresh_token的必要条件(从你的输出看已经满足)。
  • 不要硬编码密钥和授权码到代码中,建议用环境变量管理,避免敏感信息泄露。
  • 当get_refresh_token返回None时,要做容错处理,比如终止流程或提示用户重新授权。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 10:47:32