fish2018/pansou · warning

document has no share link

Error message

document has no share link

What it means

After parsing the panso document page, the code looks for the first a.jump-link[href] element holding the actual share URL. If the selector matches nothing or href is empty, the page structure changed or the resource has no link, so this error is thrown rather than returning a result with an empty link.

Solutions

  1. Dump the HTML on failure and update the CSS selector to match the site's current markup
  2. Add fallback selectors (try multiple candidate selectors before giving up)
  3. Skip the item gracefully (return a sentinel/empty result) instead of failing the whole search
  4. Check whether the returned page is actually a redirect/login page and handle that case separately

Example fix

// before
linkURL := strings.TrimSpace(doc.Find("a.jump-link[href]").First().AttrOr("href", ""))
if linkURL == "" {
    return model.SearchResult{}, fmt.Errorf("document has no share link")
}
// after
linkURL := strings.TrimSpace(doc.Find("a.jump-link[href]").First().AttrOr("href", ""))
if linkURL == "" {
    linkURL = strings.TrimSpace(doc.Find(".down-link, a.btn-go[href]").First().AttrOr("href", ""))
}
if linkURL == "" {
    return model.SearchResult{}, fmt.Errorf("document has no share link (selector may be stale)")
}
Defensive patterns

Strategy: validation

Validate before calling

sel := doc.Find("a.jump-link[href]")
if sel.Length() == 0 || strings.TrimSpace(sel.First().AttrOr("href", "")) == "" {
    return fmt.Errorf("page has no jump link; selector may be stale")
}

Try / catch

res, err := fetchPansoDocument(...)
if err != nil {
    if errors.Is(err, ErrNoShareLink) { continue /* skip item */ }
    return err
}

Prevention

When it happens

Trigger: goquery doc.Find("a.jump-link[href]") yields no elements, or the first match's href attribute is empty/whitespace after TrimSpace.

Common situations: The panso site redesigned its DOM and renamed/removed the jump-link class; the resource page is a stub or paywalled page without a share link; an interstitial/consent page was served instead of the resource page.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at plugin/sousou/sousou.go:265

	if err != nil {
		return model.SearchResult{}, err
	}
	setSousouWebHeaders(req, SousouWebURL+"?q=")
	resp, err := client.Do(req)
	if err != nil {
		return model.SearchResult{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return model.SearchResult{}, fmt.Errorf("document returned status %d", resp.StatusCode)
	}
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return model.SearchResult{}, err
	}
	linkURL := strings.TrimSpace(doc.Find("a.jump-link[href]").First().AttrOr("href", ""))
	if linkURL == "" {
		return model.SearchResult{}, fmt.Errorf("document has no share link")
	}
	linkType := util.GetLinkType(linkURL)
	if linkType == "others" || linkType == "" {
		return model.SearchResult{}, fmt.Errorf("unsupported share link: %s", linkURL)
	}
	title := cleanPansoTitle(doc.Find(".resource-box h1").First().Text())
	if title == "" {
		title = cleanPansoTitle(item.Title)
	}
	datetime := item.Datetime
	if value := strings.TrimSpace(doc.Find(".description-label").FilterFunction(func(_ int, s *goquery.Selection) bool {
		return strings.TrimSpace(s.Text()) == "分享时间"
	}).Next().Text()); value != "" {
		if parsed, err := time.Parse("2006-01-02", value); err == nil {
			datetime = parsed
		}
	}
	if datetime.IsZero() {

View on GitHub (pinned to beaa561337)