hmjz100/LinkSwift · error · Error

[迅雷云盘] 获取令牌失败

Error message

[迅雷云盘] 获取令牌失败

What it means

Wrapper error in 迅雷云盘 (Xunlei Cloud) getFileUrl: before fetching a file's download URL it calls this.getToken(false, false) to obtain the in-memory token. If that call rejects with a non-Error value, it is re-thrown as '[迅雷云盘] 获取令牌失败'. The real reason (expired token, failed refresh) is in the original rejection value.

Source

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

						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, {
				"Authorization": `${token.credentials?.token_type} ${token.credentials?.access_token}`,
				"Content-Type": "application/json",
				"X-Captcha-Token": token.captcha.token,
				"X-Device-Id": token.device_id
			});

			if (res.web_content_link) {
				return { index, downloadUrl: res.web_content_link };
			} else {
				// 令牌过期
				if (res.error_code == 9) {
					// 递归重试一次,isRetry=true 防止死循环
					if (!isRetry) {
						await this.getToken(true, false);
						return await this.getFileUrl(item, index, true);

View on GitHub (pinned to 417ea5e28a)

Solutions

  1. Reload the cloud drive page to rebuild the in-memory token state, then retry
  2. Log in to Xunlei Cloud again in the userscript to refresh credentials
  3. Check the console for the original rejection payload to identify the true refresh failure
  4. Fix the token source so it throws Error instances with meaningful messages instead of raw objects

Example fix

// before
tokenPromise.catch(e => { throw e; });
// after
tokenPromise.catch(e => { throw e instanceof Error ? e : new Error("获取令牌失败: " + JSON.stringify(e)); });
Defensive patterns

Strategy: retry

Validate before calling

const token = getCachedXunleiToken();
if (!token || token.expiresAt <= Date.now()) { await refreshToken(); }

Type guard

function hasValidToken(t) { return Boolean(t?.credentials?.access_token && t?.captcha?.token); }

Try / catch

try { return await getFileUrl(item, i); } catch (e) { if (e.message.includes('获取令牌失败')) { await refreshToken(); return getFileUrl(item, i); } throw e; }

Prevention

When it happens

Trigger: getFileUrl(item, index) called for a file without a cached downloadUrl; getToken(false, false) rejects with a non-Error — usually because the cached access token expired and the silent refresh path returned a non-Error rejection.

Common situations: Long-lived browser tab where the Xunlei access token expired mid-session; Xunlei API returning a JSON error object (not an Error) on refresh; rate limiting on the token endpoint; user logged out on another device invalidating the token.

Related errors


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