siyuan-note/siyuan · error
decode [h] failed
Error message
decode [h] failed: %s
What it means
The optional `h` query param of the forward-proxy endpoint carries request headers as base64 RawURLEncoding of a JSON object map[string][]string. If base64 decoding of `h` fails, the handler returns "decode [h] failed: <reason>".
Solutions
- Encode the headers JSON with base64.RawURLEncoding (no padding, URL-safe alphabet)
- Only send `h` when you actually have headers; omit the param entirely otherwise (it is optional)
- Round-trip decode the value in your client to confirm correctness before the request
Example fix
// before
const h = btoa(JSON.stringify({"X-Token": ["abc"]})); // std base64 with padding
// after
const h = btoa(JSON.stringify({"X-Token": ["abc"]})).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); Defensive patterns
Strategy: validation
Validate before calling
const h = headers && Object.keys(headers).length ? toBase64Url(JSON.stringify(headers)) : null;
Type guard
function isValidBase64Url(s) {
return /^[A-Za-z0-9_-]+$/.test(s);
} Prevention
- Omit the h param entirely when no headers are needed
- Use one shared base64url+JSON encoder for header maps
When it happens
Trigger: Supplying `h` that is not valid RawURLEncoding base64: standard base64 with padding or +// characters, mangling by URL encoding, or truncation.
Common situations: Custom scripts that JSON-encode headers but use stdEncoding base64; shell quoting stripping characters; hand-copying an encoded value from logs.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- decode [u] failed
- parse [h] failed
- 344
- agent HTTP tools support HTTP, HTTPS and SOCKS5 proxies
- automatic proxy configuration is not supported
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/477fbf6e290499e5.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/network.go:358
}
uBytes, decErr := base64.RawURLEncoding.DecodeString(uParam)
if decErr != nil {
err = fmt.Errorf("decode [u] failed: %s", decErr.Error())
return
}
parsedURL, err = url.ParseRequestURI(string(uBytes))
if err != nil {
err = fmt.Errorf("parse [u] failed: %s", err.Error())
return
}
h := http.Header{}
headers = &h
hParam := c.Query("h")
if hParam != "" {
hBytes, decErr := base64.RawURLEncoding.DecodeString(hParam)
if decErr != nil {
err = fmt.Errorf("decode [h] failed: %s", decErr.Error())
return
}
var record map[string][]string
if jsonErr := json.Unmarshal(hBytes, &record); jsonErr != nil {
err = fmt.Errorf("parse [h] failed: %s", jsonErr.Error())
return
}
for k, vs := range record {
for _, v := range vs {
h.Add(k, v)
}
}
}
timeout = 30 * time.Second
tParam := c.Query("t")
if tParam != "" {View on GitHub (pinned to 9f775e8a12)