{"record":{"id":"1fd3ea8eb9a0ff8d","repo":"siyuan-note/siyuan","slug":"download-custom-emoji-failed-w","errorCode":null,"errorMessage":"download custom emoji failed: %w","messagePattern":"download custom emoji failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/system.go","lineNumber":336,"sourceCode":"\t\treturn io.ReadAll(io.LimitReader(file, maxCustomEmojiSize+1))\n\t}\n\n\trawURL := strings.TrimSpace(c.PostForm(\"url\"))\n\tif rawURL == \"\" {\n\t\treturn nil, fmt.Errorf(\"field [file] or [url] must not be empty\")\n\t}\n\treturn downloadCustomEmojiData(rawURL)\n}\n\nfunc downloadCustomEmojiData(rawURL string) ([]byte, error) {\n\tparsedURL, err := url.Parse(rawURL)\n\tif err != nil || (parsedURL.Scheme != \"http\" && parsedURL.Scheme != \"https\") || parsedURL.Host == \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid custom emoji URL\")\n\t}\n\n\tresponse, err := util.NewCustomReqClient().R().Get(parsedURL.String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"download custom emoji failed: %w\", err)\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"download custom emoji failed with status %d\", response.StatusCode)\n\t}\n\tif response.ContentLength > maxCustomEmojiSize {\n\t\treturn nil, fmt.Errorf(\"custom emoji file is too large\")\n\t}\n\n\tdata, err := io.ReadAll(io.LimitReader(response.Body, maxCustomEmojiSize+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read custom emoji response failed: %w\", err)\n\t}\n\treturn data, nil\n}\n\nfunc normalizeCustomEmojiData(data []byte) (normalized []byte, ext string, err error) {\n\tif len(data) == 0 {","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/api/system.go#L318-L354","documentation":"Returned by downloadCustomEmojiData (system.go:336) when the HTTP GET to fetch the custom-emoji URL failed at the transport level (DNS, connection refused, TLS handshake, timeout, etc.). The underlying error is wrapped with %w so the cause is preserved; the response may not have been received at all, so no status code is reported.","triggerScenarios":"The emoji `url` points to a host that does not resolve, refuses the connection, has an expired/broken TLS certificate, or the request exceeded the custom-request-client timeout. util.NewCustomReqClient().R().Get(parsedURL.String()) at system.go:334 returns a non-nil err and it is wrapped at line 336.","commonSituations":"Offline or behind a firewall/proxy that blocks the emoji host. Stale/expired CDN link. Host requires auth or blocks the SiYuan user-agent. Transient network blip. Self-signed or outdated CA store causing TLS failure.","solutions":["Verify the URL is reachable from the SiYuan host (curl -I <url>) and the TLS cert is valid.","If the host is blocked by a corporate proxy, configure the kernel HTTP proxy settings and retry.","Retry once for transient failures; if it persists, download the file and use the 'file' upload mode instead.","Check the wrapped error (err.Unwrap / errors.Unwrap) for the exact transport cause (DNS, TLS, timeout)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check reachability is not guaranteed; wrap in retry with backoff\nasync function downloadWithRetry(url, n=2) {\n  for (let i=0;i<n;i++){ try { return await downloadEmoji(url); } catch(e){ if (/download custom emoji failed/.test(e.message) && i<n-1) await sleep(500*(i+1)); else throw e; } }\n}","typeGuard":null,"tryCatchPattern":"try { await uploadEmojiFromUrl(url); }\ncatch (e) {\n  if (/download custom emoji failed/.test(e.msg)) { /* inspect e.cause: DNS/TLS/timeout; fix and retry, or fall back to file upload */ }\n  else throw e;\n}","preventionTips":["Verify the URL is reachable from the SiYuan host (curl -I) before submitting.","Configure the kernel HTTP proxy if the host is behind a corporate proxy.","Fall back to the 'file' upload mode when the remote host is persistently unreachable.","Inspect the wrapped cause (errors.Unwrap) to distinguish DNS vs TLS vs timeout."],"tags":["emoji","network","kernel"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}