{"record":{"id":"1c749095a438978e","repo":"TechnitiumSoftware/DnsServer","slug":"invalid-token-or-session-expired","errorCode":null,"errorMessage":"Invalid token or session expired.","messagePattern":"Invalid token or session expired\\.","errorType":"http","errorClass":"InvalidTokenWebServiceException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/DnsWebService.cs","lineNumber":2351,"sourceCode":"        }\n\n        private async Task WebServiceApiMiddleware(HttpContext context, RequestDelegate next)\n        {\n            HttpRequest request = context.Request;\n\n            if (_clusterManager.ClusterInitialized)\n            {\n                ClusterNodeType pathNodeType = GetClusterNodeTypeForPath(request.Path);\n                switch (pathNodeType)\n                {\n                    case ClusterNodeType.Primary:\n                        //this api can be called only on primary node\n                        ClusterNode selfNode = _clusterManager.GetSelfNode();\n                        if (selfNode.Type == ClusterNodeType.Secondary)\n                        {\n                            //validate user session before proxying request\n                            if (!TryValidateSession(context, out UserSession session))\n                                throw new InvalidTokenWebServiceException(\"Invalid token or session expired.\");\n\n                            //proxy to primary node\n                            ClusterNode primaryNode = _clusterManager.GetPrimaryNode();\n                            await primaryNode.ProxyRequest(context, session.User.Username);\n                            return;\n                        }\n\n                        break;\n\n                    case ClusterNodeType.Secondary:\n                        //this api must be called on current node\n                        break;\n\n                    default:\n                        //this api can be called on any specified node\n                        string nodeName = request.GetQueryOrForm(\"node\", null);\n                        if (!string.IsNullOrEmpty(nodeName) && (nodeName != \"cluster\"))\n                        {","sourceCodeStart":2333,"sourceCodeEnd":2369,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/DnsWebService.cs#L2333-L2369","documentation":"InvalidTokenWebServiceException thrown in WebServiceApiMiddleware when a Primary-node-only API is requested on a Secondary cluster node and TryValidateSession fails. The session must be validated before the request is proxied to the Primary node; an invalid/expired token (or none) results in this error instead of a blind proxy.","triggerScenarios":"In a cluster, a client calls an API classified ClusterNodeType.Primary while connected to a Secondary node, with a missing, malformed, disabled-user, or expired session token. TryValidateSession returns false (null session, disabled user, or expired).","commonSituations":"Token expired between UI open and the call; user was disabled; SSO session lapsed; load balancer routed the client to a secondary; client sent no/old Authorization header to a primary-only endpoint.","solutions":["Re-authenticate via /api/user/login or /api/user/createToken to obtain a fresh token, then retry.","Ensure the client sends the token in the header the server expects (Authorization) on every primary-only call.","Direct primary-only calls at the Primary node, or keep the cluster topology so the proxy path validates.","Confirm the user account is not disabled in auth.config."],"exampleFix":"null","handlingStrategy":"retry","validationCode":"// Ensure a non-expired token exists before calling primary-only APIs\nif (!await IsSessionAliveAsync())\n    await LoginAsync();","typeGuard":"null","tryCatchPattern":"catch (InvalidTokenWebServiceException)\n{\n    await LoginAsync(); // refresh token\n    await RetryPrimaryCallAsync(); // then retry once\n}","preventionTips":["Refresh tokens proactively before their lifetime ends.","Send the Authorization header on every cluster call.","Direct primary-only calls at the Primary node when possible."],"tags":["dns-server","technitium","auth","session","cluster","token-expiry","csharp"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}