hmjz100/LinkSwift · error · Error

错误:<br/>令牌刷新失败,

Error message

错误:<br/>令牌刷新失败,${res?.error_description || res?.error || "未知错误"}

What it means

Thrown during credential/captcha token refresh when the refresh API responds successfully at the HTTP layer but does NOT return a captcha_token. The code expects res.captcha_token plus expires_in to cache a captcha token; if the response lacks it, it surfaces the server's error_description / error fields, falling back to '未知错误'. It indicates the cloud provider refused the token refresh.

Solutions

  1. Log out and log back into the cloud drive in the userscript to regenerate credentials
  2. Read the embedded error_description in the message (e.g. invalid_grant) and act on it — most often re-authentication
  3. Clear the userscript's cached storage keys (credentials, captcha) and retry the refresh
  4. Check system clock accuracy if the error mentions expired/invalid token timestamps
Defensive patterns

Strategy: retry

Validate before calling

const creds = base.getStorage(credsKey);
if (!creds?.refresh_token) { promptLogin(); }

Type guard

function hasCaptcha(res) { return typeof res?.captcha_token === 'string' && res.captcha_token.length > 0; }

Try / catch

try { await refreshCreds(); } catch (e) { if (e.message.includes('令牌刷新失败')) { clearStoredCredentials(); promptLogin(); } else throw e; }

Prevention

When it happens

Trigger: Calling the token-refresh endpoint (POST with JSON body) and receiving a JSON body without captcha_token — e.g. {error: 'invalid_grant'} or {error_description: '...'} — typically when stored credentials (creds) are expired, revoked, or the device/client ID is rejected.

Common situations: User logged out or changed password on the cloud drive, invalidating stored refresh credentials; captcha/anti-bot policy change server-side; clock skew making cached captcha look valid; clientId/deviceId mismatch after clearing browser storage.

Related errors


AI-assisted analysis of hmjz100/LinkSwift@417ea5e28a (2026-09-02). Data as JSON: /api/errors/e2d21398ced17730. Report an issue: GitHub.

Appendix: source

Thrown at (改)网盘直链下载助手.user.js:7716

					const res = await base.post(config.$xunlei.api.getCaptchaToken, {
						client_id: clientId,
						action: "get:/drive/v1/about",
						device_id: deviceId,
						meta: {
							username: "", phone_number: "", email: "",
							package_name: location.host,
							client_version: clientVersion,
							captcha_sign: this._getCaptchaSign(clientId, clientVersion, location.host, deviceId, timestamp.toString()),
							timestamp: timestamp.toString(),
							user_id: userId
						}
					}, { "Content-Type": "application/json" });

					if (res?.captcha_token) {
						cap = { token: res.captcha_token, expires_at: new Date(timestamp + res.expires_in * 1000).toString() };
						base.setStorage(capKey, cap);
					} else {
						throw new Error(`错误:<br/>令牌刷新失败,${res?.error_description || res?.error || "未知错误"}`);
					}
				} else if (!creds || !cap || !clientId || !deviceId) {
					throw new Error(`错误:<br/>请先登录网盘后再获取文件呢~`);
				}

				return { credentials: creds, captcha: cap, device_id: deviceId };
			});
		},
		async getFileUrl(item, index, isRetry = false) {
			if (item.downloadUrl) return { index, downloadUrl: item.downloadUrl };

			// 获取当前内存中的 token
			const token = await this.getToken(false, false).catch(e => {
				if (e instanceof Error) throw e;
				throw new Error(e?.message || e || "[迅雷云盘] 获取令牌失败");
			});

			const res = await base.get(config.$xunlei.api.getLink + item.id, {

View on GitHub (pinned to 417ea5e28a)