siyuan-note/siyuan · error · Error

data?.msg || data?.message || window.siyuan.languages._kerne

Error message

data?.msg || data?.message || window.siyuan.languages._kernel[28]

What it means

Thrown by util.WebFetch in kernel/util/webfetch.go when the URL parses successfully and carries an http/https scheme, but its host component is empty. This happens with malformed inputs such as "http:///path", "https:/single-slash", or "http://?q=1", which net/url.Parse accepts but which leave u.Host empty. The check exists because an empty host would otherwise send a request to the local machine, which is both useless and an SSRF risk.

Source

Thrown at app/src/ai/editorSSE.ts:110

    onEvent: (event: TAIEditorSSEEvent) => void,
    signal: AbortSignal,
) => {
    const response = await fetch("/api/ai/editor/chat", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify(request),
        signal,
    });
    const contentType = response.headers.get("Content-Type") || "";
    if (!response.ok || !contentType.includes("text/event-stream")) {
        let message = window.siyuan.languages._kernel[28];
        try {
            const data = await response.json();
            message = data?.msg || data?.message || message;
        } catch (e) {
            // 响应不是 JSON 时使用统一错误文案。
        }
        throw new Error(message);
    }
    const reader = response.body?.getReader();
    if (!reader) {
        throw new Error(window.siyuan.languages._kernel[28]);
    }
    const decoder = new TextDecoder();
    const parserState = createAIEditorSSEParserState();
    let terminalReceived = false;
    while (true) {
        const result = await reader.read();
        if (result.done) {
            break;
        }
        parseAIEditorSSE(parserState, decoder.decode(result.value, {stream: true})).forEach(event => {
            terminalReceived = terminalReceived || event.type === "done" || event.type === "error";
            onEvent(event);
        });
    }

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Inspect the exact rawURL string passed to WebFetch; look for single-slash schemes ("https:/...") or a missing host segment
  2. Normalize the URL before calling WebFetch: run url.Parse, require u.Hostname() != "", and repair common typos such as "https:/" -> "https://"
  3. If the URL comes from an LLM or user input, validate it at the boundary and reject/repair empty-host URLs before it reaches the kernel
  4. Check for stray whitespace or embedded control characters pasted into the URL

Example fix

// before
result, err := util.WebFetch(userURL, "markdown")

// after
u, perr := url.Parse(strings.TrimSpace(userURL))
if perr != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") {
    return fmt.Errorf("invalid URL %q: must be a fully-qualified http(s) URL with a host", userURL)
}
result, err := util.WebFetch(u.String(), "markdown")
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(strings.TrimSpace(rawURL))
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
    return fmt.Errorf("reject URL %q before WebFetch: need http(s) scheme and non-empty host", rawURL)
}
result, err := util.WebFetch(u.String(), format)

Type guard

func isFetchableURL(rawURL string) bool {
    u, err := url.Parse(strings.TrimSpace(rawURL))
    return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}

Try / catch

null

Prevention

When it happens

Trigger: Calling util.WebFetch(rawURL, format) with a scheme-relative or single-slash URL like "https:/example.com/a" (parses with empty Host), "http:///foo", or a URL built by string concatenation where the host segment was accidentally dropped or contained unescaped characters that broke host parsing.

Common situations: Agent/AI features or plugins passing user-typed or LLM-generated URLs that were truncated or copy-pasted with a missing slash; URLs assembled via fmt.Sprintf where the host variable was empty; input like "https://" passed straight through from a chat prompt.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/a246c0ac69d48cf3. Report an issue: GitHub.