Tencent/WeKnora · error
yuque api error: status=%d body=%s
Error message
yuque api error: status=%d body=%s
What it means
Same branch as the msg variant: doRequest gets a non-2xx status (outside 401/403/429/5xx) but the body either isn't JSON or has no parseable "message" field, so the client falls back to embedding a truncated (500-char) raw body in the error. This is the diagnostic fallback that tells you Yuque returned an unrecognized error payload. It is returned un-retried to every caller of doRequest.
Source
Thrown at internal/datasource/connector/yuque/client.go:140
}
continue
}
return lastErr
}
// 401/403 → surface as ErrInvalidCredentials so DataSourceService can
// distinguish bad-token from transient failures and auto-flag the source.
if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
return fmt.Errorf("%w: status=%d body=%s", datasource.ErrInvalidCredentials, resp.StatusCode, bodyPreview)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
var apiErr apiErrorBody
_ = json.Unmarshal(body, &apiErr)
if apiErr.Message != "" {
return fmt.Errorf("yuque api error: status=%d msg=%s", resp.StatusCode, apiErr.Message)
}
return fmt.Errorf("yuque api error: status=%d body=%s", resp.StatusCode, bodyPreview)
}
if result != nil {
if err := json.Unmarshal(body, result); err != nil {
return fmt.Errorf("decode response: %w", err)
}
}
return nil
}
return lastErr
}
// parseRetryAfter returns the Retry-After duration from the header, or fallback if unparseable.
// Retry-After: "0" (or negative) is coerced to 100ms so we still yield and don't busy-retry.
// Note: only integer-seconds form is supported (RFC 7231 also allows HTTP-date — not seen from Yuque).
func parseRetryAfter(header string, fallback time.Duration) time.Duration {
if header == "" {
return fallbackView on GitHub (pinned to 988cbb0330)
Solutions
- Inspect the body= content in the error — it identifies who actually produced the response (HTML → proxy/nginx; JSON with a different shape → API version mismatch).
- If the body is an HTML block page, fix the network path: check proxy env vars (HTTP_PROXY/HTTPS_PROXY), corporate egress rules, or the Yuque instance's fronting nginx config.
- Verify baseURL is exactly the API root (e.g. https://www.yuque.com/api/v2 or the self-hosted equivalent); wrong roots return non-JSON 404s.
- Capture the full response with curl -i to see headers and confirm which server answered.
- If the body shows a message under a non-standard key, extend apiErrorBody to match your Yuque version's error format.
Example fix
// before baseURL: "https://www.yuque.com" // page host, API paths then 404 with HTML // after baseURL: "https://www.yuque.com/api/v2" // or set api_base_url in the datasource config
Defensive patterns
Strategy: type-guard
Validate before calling
// Go: probe the base URL for a JSON-capable API before configuring
resp, err := http.Get(baseURL + "/api/v2/user")
if err == nil {
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
// a proxy/html responder sits in front — fix network path or base URL
}
} Type guard
// Go: detect the raw-body fallback variant
func isYuqueRawBodyError(err error) (bodySnippet string, ok bool) {
if err == nil {
return "", false
}
if strings.HasPrefix(err.Error(), "yuque api error: status=") && strings.Contains(err.Error(), "body=") {
i := strings.Index(err.Error(), "body=")
return err.Error()[i+5:], true
}
return "", false
} Try / catch
err := cli.Ping(ctx)
if err != nil {
if body, ok := isYuqueRawBodyError(err); ok {
if strings.Contains(strings.ToLower(body), "<html") {
return fmt.Errorf("a proxy/firewall intercepted the Yuque request, check network egress: %s", body)
}
}
return err
} Prevention
- Pin the baseURL to the API root (https://www.yuque.com/api/v2 or your instance's equivalent) and never a page URL.
- Check Content-Type of responses during setup to detect intercepting proxies early.
- Set NO_PROXY/proxy env correctly in containerized deployments so corporate proxies don't answer for Yuque.
- Keep apiErrorBody in sync with your Yuque version's error JSON shape so the msg branch is preferred over the body fallback.
When it happens
Trigger: A non-2xx response whose body lacks the {"message":"..."} shape: an HTML error page from a proxy/reverse-proxy/gateway, an empty body, or a Yuque error format the apiErrorBody struct doesn't match (e.g. message nested under a different key), for any endpoint from Ping through GetDocDetail.
Common situations: A corporate proxy or WAF intercepts the request and returns an HTML 403/400 block page; a self-hosted Yuque (or its nginx front) emits non-JSON error pages; a typo'd baseURL (e.g. missing path prefix) yields a non-JSON 404; an API gateway rate-limits or errors with a custom payload.
Related errors
- yuque api error: status=%d msg=%s
- list team repos: %w
- list personal repos: %w
- list docs for book %d: %w
- failed to generate questions: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/7a8df28795a3f0c6.
Report an issue: GitHub.