sipeed/picoclaw · error
decode JSON response: %w
Error message
decode JSON response: %w
What it means
The WeCom endpoint returned HTTP 200 but the body was not valid JSON for the target struct. Typical causes: the server (or an intercepting proxy/captive portal) returned an HTML page with 200, an empty body, a BOM prefix, or a payload whose types mismatch (string where a number is expected). This error propagates wrapped inside 124/131 depending on which call site hit it.
Source
Thrown at cmd/picoclaw/internal/auth/wecom.go:411
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 8192))
if readErr != nil {
return fmt.Errorf("unexpected status %s", resp.Status)
}
return fmt.Errorf("unexpected status %s: %s", resp.Status, strings.TrimSpace(string(body)))
}
if err := json.NewDecoder(resp.Body).Decode(out); err != nil {
return fmt.Errorf("decode JSON response: %w", err)
}
return nil
}
func wecomPlatformCode() int {
switch runtime.GOOS {
case "darwin":
return 1
case "windows":
return 2
case "linux":
return 3
default:
return 0
}
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- curl the endpoint from the same machine/network and inspect what the body actually is
- Connect from an unrestricted network or disable the interfering proxy/VPN
- Complete any captive-portal login, then retry the WeCom login
- Update picoclaw if the relay's JSON schema changed (type mismatches decode as JSON errors)
Defensive patterns
Strategy: try-catch
Validate before calling
// Sniff the body before decoding when a proxy is suspected
probe, _ := io.ReadAll(io.LimitReader(resp.Body, 1))
if len(probe) == 1 && probe[0] == '<' {
return errors.New("received HTML instead of JSON — captive portal or proxy interception")
} Type guard
func isJSONDecodeErr(err error) bool {
var syn *json.SyntaxError
var typeErr *json.UnmarshalTypeError
return errors.As(err, &syn) || errors.As(err, &typeErr)
} Try / catch
if err := json.NewDecoder(resp.Body).Decode(out); err != nil {
if isJSONDecodeErr(err) {
// likely proxy/captive portal: advise network change instead of retrying blindly
}
return fmt.Errorf("decode JSON response: %w", err)
} Prevention
- Test from an unrestricted network when HTML-instead-of-JSON appears
- Complete captive-portal sign-ins before interactive logins
- Disable SSL-inspecting proxies for the relay domain
When it happens
Trigger: Captive portal/Wi-Fi sign-in page served instead of the API response; transparent proxy rewriting responses; relay returning an empty 200 during degradation; upstream response schema type change (string errcode vs int) breaking Unmarshal.
Common situations: Hotel/airport Wi-Fi intercepting HTTPS via DNS tricks; corporate SSL-inspection proxy injecting its own 200 page; relay partial outage; picoclaw's response structs outdated versus relay.
Related errors
- parse response: %w
- failed to load config: %w
- failed to get WeCom QR code: %w
- failed to query WeCom QR result: %w
- unexpected status %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/819e1470401329b8.
Report an issue: GitHub.