{"record":{"id":"82893b710dc44162","repo":"SignalR/SignalR","slug":"invalid-cursor","errorCode":null,"errorMessage":"Invalid cursor.","messagePattern":"Invalid cursor\\.","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs","lineNumber":135,"sourceCode":"            return sb.ToString();\n        }\n\n        public static List<Cursor> GetCursors(string cursor, string prefix)\n        {\n            return GetCursors(cursor, prefix, s => s);\n        }\n\n        public static List<Cursor> GetCursors(string cursor, string prefix, Func<string, string> keyMaximizer)\n        {\n            return GetCursors(cursor, prefix, (key, state) => ((Func<string, string>)state).Invoke(key), keyMaximizer);\n        }\n\n        public static List<Cursor> GetCursors(string cursor, string prefix, Func<string, object, string> keyMaximizer, object state)\n        {\n            // Technically GetCursors should never be called with a null value, so this is extra cautious\n            if (String.IsNullOrEmpty(cursor))\n            {\n                throw new FormatException(Resources.Error_InvalidCursorFormat);\n            }\n\n            // If the cursor does not begin with the prefix stream, it isn't necessarily a formatting problem.\n            // The cursor with a different prefix might have had different, but also valid, formatting.\n            // Null should be returned so new cursors will be generated\n            if (!cursor.StartsWith(prefix, StringComparison.Ordinal))\n            {\n                return null;\n            }\n\n            var signals = new HashSet<string>();\n            var cursors = new List<Cursor>();\n            string currentKey = null;\n            string currentEscapedKey = null;\n            ulong currentId;\n            bool escape = false;\n            bool consumingKey = true;\n            var sb = new StringBuilder();","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/SignalR/SignalR/blob/693053b89a9e1f5ce819e3233ed159a6409de22b/src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs#L117-L153","documentation":"Thrown by Cursor.GetCursors when the cursor string is null or empty. Cursors are opaque tokens SignalR uses to track how many messages a client has consumed from each topic/stream; the client echoes them back on reconnect. A null/empty cursor means the client sent nothing or the server-side caller bypassed normal flow. This is treated as a hard format error (unlike a wrong-prefix cursor, which returns null gracefully).","triggerScenarios":"GetCursors is invoked with a null or zero-length cursor argument. This can happen if a client reconnect request omits the cursor (messageId) query parameter entirely, or if internal code passes null instead of letting the framework generate a fresh cursor set.","commonSituations":"A misbehaving or very old client that does not send a message cursor on poll/reconnect; a custom host that strips the cursor parameter; a proxy that drops query-string parameters; manual testing with a hand-crafted URL missing the cursor.","solutions":["Verify the client sends the cursor (messageId) query parameter on every reconnect/poll request after the initial negotiate.","If you are calling GetCursors directly, guard against null/empty before calling and pass a valid cursor or skip the call.","Check for proxy/load-balancer URL rewriting that strips query parameters.","Update the client library to a version compatible with the server."],"exampleFix":"// before — caller passes null cursor directly\nvar cursors = Cursor.GetCursors(null, prefix);\n\n// after — guard before calling\nif (String.IsNullOrEmpty(cursor)) {\n    cursors = null; // let the framework generate new cursors\n} else {\n    cursors = Cursor.GetCursors(cursor, prefix);\n}","handlingStrategy":"validation","validationCode":"// Check cursor before calling GetCursors\nif (string.IsNullOrEmpty(cursor)) {\n    // Do not call GetCursors; return null to let framework generate new cursors\n    return null;\n}\nvar cursors = Cursor.GetCursors(cursor, prefix);","typeGuard":null,"tryCatchPattern":"try {\n    var cursors = Cursor.GetCursors(cursor, prefix);\n} catch (FormatException ex) when (ex.Message.Contains(\"Invalid cursor\")) {\n    // treat as a fresh connection — generate new cursors\n    logger.Warn(\"Client sent empty/invalid cursor, regenerating\", ex);\n    cursors = null;\n}","preventionTips":["Guard against null/empty cursor before calling GetCursors.","Ensure clients always send the cursor parameter after the initial negotiate handshake.","Log missing-cursor occurrences to identify misbehaving clients."],"tags":["cursor","protocol","signalr","validation"],"backgroundTag":null,"analyzedSha":"693053b89a9e1f5ce819e3233ed159a6409de22b","analyzedAt":"2026-08-13T22:23:59.793Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}