{"record":{"id":"d0b4b4dcf28ff58d","repo":"Tencent/WeKnora","slug":"gateway-url-must-be-a-valid-wss-url","errorCode":null,"errorMessage":"gateway_url must be a valid wss URL","messagePattern":"gateway_url must be a valid wss URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/qqbot/client.go","lineNumber":98,"sourceCode":"\tif err != nil || u.Host == \"\" {\n\t\treturn fmt.Errorf(\"invalid qqbot api_base_url: must be a valid http(s) URL\")\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn fmt.Errorf(\"invalid qqbot api_base_url: must use http or https\")\n\t}\n\tif err := secutils.ValidateURLForSSRF(raw); err != nil {\n\t\treturn fmt.Errorf(\"invalid qqbot api_base_url: %w (for private deployments, add the hostname to SSRF_WHITELIST)\", err)\n\t}\n\treturn nil\n}\n\nfunc validateGatewayURL(raw string) error {\n\tif strings.TrimSpace(raw) == \"\" {\n\t\treturn nil\n\t}\n\tu, err := url.Parse(raw)\n\tif err != nil || u.Host == \"\" {\n\t\treturn fmt.Errorf(\"gateway_url must be a valid wss URL\")\n\t}\n\tif u.Scheme != \"wss\" {\n\t\treturn fmt.Errorf(\"gateway_url must use wss\")\n\t}\n\tcheckURL := *u\n\tcheckURL.Scheme = \"https\"\n\tif err := secutils.ValidateURLForSSRF(checkURL.String()); err != nil {\n\t\treturn fmt.Errorf(\n\t\t\t\"gateway_url failed SSRF validation: %w (for private deployments, add the hostname to SSRF_WHITELIST)\",\n\t\t\terr,\n\t\t)\n\t}\n\treturn nil\n}\n\nfunc (c *Client) SendC2CMessage(ctx context.Context, openID, content, msgID string) error {\n\tpath := fmt.Sprintf(\"/v2/users/%s/messages\", openID)\n\treturn c.sendText(ctx, path, content, msgID)","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/qqbot/client.go#L80-L116","documentation":"Validation helper validateGatewayURL rejects a configured QQ bot gateway_url that fails URL parsing or has no host. Empty values are allowed (the default gateway is used), so this only fires on a non-empty but malformed override.","triggerScenarios":"gateway_url is set to a string that url.Parse rejects or that yields an empty Host (e.g. \"wss://\", \"://bad\", a bare path, or whitespace-mangled value).","commonSituations":"Typo in the gateway URL; missing host after copying from docs; env var interpolated as empty/partial value; trailing characters breaking the URL.","solutions":["Set gateway_url to a complete URL like wss://api.sgroup.qq.com/websocket","Ensure the host portion is present and the value has no stray whitespace or scheme typos","Leave gateway_url empty to let the client discover the gateway from the API"],"exampleFix":"// before\nGATEWAY_URL=wss://   // empty host\n// after\nGATEWAY_URL=wss://api.sgroup.qq.com/websocket","handlingStrategy":"validation","validationCode":"func validGateway(u string) bool {\n    p, err := url.Parse(strings.TrimSpace(u))\n    return err == nil && p.Host != \"\" && p.Scheme == \"wss\"\n}","typeGuard":null,"tryCatchPattern":"client, err := NewClient(cfg)\nif err != nil && strings.Contains(err.Error(), \"gateway_url must be a valid wss URL\") {\n    return fmt.Errorf(\"fix GATEWAY_URL config: %w\", err)\n}","preventionTips":["Validate gateway_url with url.Parse at config load","Leave gateway_url empty to use API discovery","Avoid hand-concatenated URL strings"],"tags":["configuration","websocket","qqbot","url-parsing"],"backgroundTag":"invalid-url-config","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}