{"record":{"id":"31333a6786595e2a","repo":"netbirdio/netbird","slug":"an-earlier-read-of-the-policy-table-has-not-return","errorCode":null,"errorMessage":"an earlier read of the policy table has not returned","messagePattern":"an earlier read of the policy table has not returned","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/debug/nrpt_windows.go","lineNumber":74,"sourceCode":"// registryValue is a name and its rendered value, shared by the registry and\n// policy table readers so both anonymize by value name the same way.\ntype registryValue struct {\n\tname  string\n\tvalue string\n}\n\n// effectiveNRPTPolicies reads the effective NRPT table. The call is bounded\n// because a WMI provider can block indefinitely and a debug bundle must not.\nfunc effectiveNRPTPolicies() ([]nrptPolicyEntry, error) {\n\ttype result struct {\n\t\ttext string\n\t\terr  error\n\t}\n\n\tselect {\n\tcase nrptQueryInFlight <- struct{}{}:\n\tdefault:\n\t\treturn nil, errors.New(\"an earlier read of the policy table has not returned\")\n\t}\n\n\tdone := make(chan result, 1)\n\tgo func() {\n\t\t// the slot is released here rather than by the caller, so a read that\n\t\t// outlives the timeout holds it until the provider answers\n\t\tdefer func() { <-nrptQueryInFlight }()\n\n\t\ttext, err := nrptPolicyTableText()\n\t\tdone <- result{text: text, err: err}\n\t}()\n\n\tselect {\n\tcase res := <-done:\n\t\tif res.err != nil {\n\t\t\treturn nil, res.err\n\t\t}\n\t\treturn parseNRPTPolicyTable(res.text), nil","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/debug/nrpt_windows.go#L56-L92","documentation":"errValidationUnavailable branch of the scheme-flow validation (middleware.go:581): the token belongs to an OIDC-method scheme, validation must go through the sessionValidator gRPC (ValidateSession, middleware.go:693-698), and that RPC errored. The client sees 502 'authentication service unavailable'; the scheme type is recorded in captured data.","triggerScenarios":"Login form (or programmatic token submission) on an OIDC-schemed domain completes, but when the proxy calls management's ValidateSession the gRPC call fails: management down, deadline exceeded, connection refused, TLS failure.","commonSituations":"Same class as index 7: management restarts/outages, proxy-to-management network issues, cert rotation breaking mTLS; typically many simultaneous 502s across all OIDC domains.","solutions":["Restore management availability; verify from the proxy host that the management gRPC endpoint answers.","Inspect the chained error after 'session validation unavailable' in the logs for dial/TLS/deadline specifics.","Re-dial or restart the proxy if the gRPC channel did not recover after management came back.","Alert on this 502 signature to catch proxy-management splits early."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Same transient class as header-token 502: ValidateSession gRPC failed.\n// Retry the token submission with backoff once management is reachable.\nfor i := 0; i < 3; i++ {\n    resp, err = client.PostForm(loginURL, vals)\n    if err == nil && resp.StatusCode != http.StatusBadGateway {\n        break\n    }\n    time.Sleep((1 << i) * time.Second)\n}","preventionTips":["Sequence management restarts so at least one instance serves ValidateSession.","Verify proxy-to-management mTLS certificates on both sides after rotation.","Alert on 502 across OIDC domains as a management-outage signal.","Retries are safe; the submitted credentials and token are unaffected."],"tags":["proxy","grpc","session-validation","availability","oidc"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}