{"record":{"id":"82279ac5a1d102ee","repo":"Wei-Shaw/sub2api","slug":"xai-oauth-redirect-missing-location","errorCode":null,"errorMessage":"xAI OAuth redirect missing Location","messagePattern":"xAI OAuth redirect missing Location","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/internal/pkg/xai/sso_device.go","lineNumber":289,"sourceCode":"\t\tif err != nil {\n\t\t\treturn 0, currentURL, nil, err\n\t\t}\n\t\tf.captureCookies(request.URL, response)\n\t\tdata, readErr := io.ReadAll(io.LimitReader(response.Body, ssoMaxAuthBody+1))\n\t\t_ = response.Body.Close()\n\t\tif readErr != nil {\n\t\t\treturn response.StatusCode, currentURL, nil, readErr\n\t\t}\n\t\tif len(data) > ssoMaxAuthBody {\n\t\t\treturn response.StatusCode, currentURL, nil, errors.New(\"xAI OAuth response exceeds 2 MiB\")\n\t\t}\n\t\tif response.StatusCode < 300 || response.StatusCode > 399 {\n\t\t\treturn response.StatusCode, currentURL, data, nil\n\t\t}\n\n\t\tlocation := strings.TrimSpace(response.Header.Get(\"Location\"))\n\t\tif location == \"\" {\n\t\t\treturn response.StatusCode, currentURL, data, errors.New(\"xAI OAuth redirect missing Location\")\n\t\t}\n\t\tbase, _ := url.Parse(currentURL)\n\t\tnext, err := url.Parse(location)\n\t\tif err != nil {\n\t\t\treturn response.StatusCode, currentURL, data, err\n\t\t}\n\t\tcurrentURL = base.ResolveReference(next).String()\n\t\tif !safeXAIAuthURL(currentURL) {\n\t\t\treturn response.StatusCode, currentURL, data, errors.New(\"xAI OAuth redirected to untrusted host\")\n\t\t}\n\t\tif response.StatusCode == http.StatusSeeOther || ((response.StatusCode == http.StatusMovedPermanently || response.StatusCode == http.StatusFound) && currentMethod != http.MethodGet && currentMethod != http.MethodHead) {\n\t\t\tcurrentMethod = http.MethodGet\n\t\t\tcurrentForm = nil\n\t\t}\n\t}\n\treturn 0, currentURL, nil, errors.New(\"xAI OAuth redirected too many times\")\n}\n","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/pkg/xai/sso_device.go#L271-L307","documentation":"The device flow handles redirects manually (up to 8 hops). When a 3xx response has an empty or whitespace-only Location header, there is no way to continue, so the request fails with this error. A well-behaved OAuth server always sends Location on 3xx.","triggerScenarios":"Any step of the flow receives 300-399 whose Location header is missing/blank: broken server-side redirect config, a proxy stripping Location, or a 304-like status used oddly.","commonSituations":"Reverse proxies or TLS terminators dropping the Location header; xAI misconfiguration during incidents; HTTP/1.0 intermediaries that mishandle redirects.","solutions":["Replay the request with curl -v (same cookies) to inspect the raw 3xx and its headers.","Remove intermediary proxies from the path for the OAuth hosts.","Retry once; transient infra faults often clear.","If xAI is genuinely returning headerless 3xx, it is a server-side incident — wait or report."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"redirect missing Location\") {\n    log.Printf(\"3xx without Location from %s; retrying once\", endpoint)\n    err = flow.Start(ctx)\n}","preventionTips":["Keep intermediaries (custom proxies, rewriters) off the OAuth path","curl -v the endpoint when it recurs","Treat as transient infra noise unless persistent"],"tags":["xai","sso","http","redirect"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}