fish2018/pansou · error

盘链 token 未解析出真实链接

Error message

盘链 token 未解析出真实链接

What it means

resolvePanToken returns '盘链 token 未解析出真实链接' ('panlian token did not resolve to a real link') after fetching/decoding the token page failed to yield a string passing isRealPanURL. The token exists but the real pan URL could not be extracted from the resolution response.

Solutions

  1. Log the raw resolution response for the failing token and compare against the extraction regex; update the pattern if the format changed.
  2. Verify the token resolves in a browser — if not, the share is dead; skip the entry.
  3. Extend isRealPanURL to cover pan hosts panlian now emits.
  4. Add retry with a fresh client in case the first fetch was served an anti-bot page.

Example fix

// before
return "", fmt.Errorf("盘链 token 未解析出真实链接")
// after
return "", fmt.Errorf("盘链 token 未解析出真实链接 (token=%s, body=%.200s)", token, lastBody)
Defensive patterns

Strategy: fallback

Validate before calling

// sanity-check token before spending a resolution request
if token == "" || len(token) > 2048 {
    return fmt.Errorf("malformed panlian token")
}

Try / catch

url, err := plugin.ResolveToken(ctx, token)
if err != nil && strings.Contains(err.Error(), "未解析出真实链接") {
    log.Printf("token unresolved (possibly dead share): %v", err)
    return nil // degrade gracefully, keep other links
}

Prevention

When it happens

Trigger: The token resolution HTTP call succeeds but the page/JSON contains no recognizable pan URL (regex misses), the token is stale/revoked, decodePanURL output fails isRealPanURL, or the upstream changed its redirect/embed format.

Common situations: Deleted or expired panlian shares; panlian changed its HTML/JS so the extraction regex no longer matches; anti-bot serving an interstitial instead of the link page; token pointing to a supported-but-changed pan host.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/a203198dc79f066d. Report an issue: GitHub.

Appendix: source

Thrown at plugin/panlian/panlian.go:979

	}
	defer resp.Body.Close()
	if location := strings.TrimSpace(resp.Header.Get("Location")); isRealPanURL(location) {
		return decodePanURL(location), nil
	}
	body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if err != nil {
		return "", err
	}
	for _, pattern := range panTokenURLRegexes {
		match := pattern.FindSubmatch(body)
		if len(match) > 1 {
			resolvedURL := decodePanURL(string(match[1]))
			if isRealPanURL(resolvedURL) {
				return resolvedURL, nil
			}
		}
	}
	return "", fmt.Errorf("盘链 token 未解析出真实链接")
}

func decodePanURL(value string) string {
	value = html.UnescapeString(strings.TrimSpace(value))
	return strings.ReplaceAll(value, `\/`, `/`)
}

func (p *PanlianPlugin) setPanlianHeaders(req *http.Request, cookie string, referer string) {
	req.Header.Set("User-Agent", browserUserAgent())
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("Accept-Language", "zh-TW,zh;q=0.9,zh-CN;q=0.8,en;q=0.7")
	req.Header.Set("Origin", DefaultBaseURL)
	req.Header.Set("Referer", referer)
	if cookie != "" {
		req.Header.Set("Cookie", cookie)
	}
}

View on GitHub (pinned to beaa561337)