{"record":{"id":"fd47bf6bb1bd42da","repo":"git-ecosystem/git-credential-manager","slug":"state-key-cannot-start-with-gcmstateprefix-th","errorCode":null,"errorMessage":"State key cannot start with '{GcmStatePrefix}'; the prefix is reserved and added automatically when state is emitted.","messagePattern":"State key cannot start with '(.+?)'; the prefix is reserved and added automatically when state is emitted\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Core/GitStateValidation.cs","lineNumber":98,"sourceCode":"    {\n        if (string.IsNullOrWhiteSpace(key))\n        {\n            throw new ArgumentException(\"State key cannot be null or whitespace.\", nameof(key));\n        }\n\n        foreach (char c in key)\n        {\n            if (c == EQ || c == LF || c == NUL)\n            {\n                throw new ArgumentException(\n                    \"State key cannot contain '=', newline, or NUL characters.\",\n                    nameof(key));\n            }\n        }\n\n        if (key.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal))\n        {\n            throw new ArgumentException(\n                $\"State key cannot start with '{Constants.CredentialProtocol.GcmStatePrefix}'; \" +\n                \"the prefix is reserved and added automatically when state is emitted.\",\n                nameof(key));\n        }\n    }\n\n    /// <summary>\n    /// Throws <see cref=\"ArgumentException\"/> if <paramref name=\"value\"/> is\n    /// not a legal state entry value.\n    /// </summary>\n    public static void ValidateValue(string value)\n    {\n        if (value is null)\n        {\n            throw new ArgumentNullException(nameof(value), \"State value cannot be null.\");\n        }\n\n        foreach (char c in value)","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/Core/GitStateValidation.cs#L80-L116","documentation":"The 'gcm-' state prefix (Constants.CredentialProtocol.GcmStatePrefix) is reserved: it is added automatically when state is emitted on the wire, so user-supplied keys must be stored WITHOUT the prefix. Passing a key that already starts with the prefix would double-prefix it, so ValidateKey throws ArgumentException.","triggerScenarios":"Calling SetState/WithState with a key like 'gcm-foo' (or whatever GcmStatePrefix is) — typically because the developer saw emitted state lines prefixed and copied that form back in.","commonSituations":"Round-tripping state: reading an emitted state line (e.g. 'gcm-xyz=1') and feeding the full prefixed key back into SetState on a subsequent invocation instead of stripping the prefix.","solutions":["Strip the prefix before storing: key.StartsWith(prefix) ? key.Substring(prefix.Length) : key.","Pass only the bare suffix as the state key; the library adds the prefix on emission.","Normalize state keys on ingestion when reading them back from a previous response."],"exampleFix":"// before\nresponse.SetState(\"gcm-oauth-round\", \"1\");\n// after\nresponse.SetState(\"oauth-round\", \"1\"); // prefix added automatically on emit","handlingStrategy":"validation","validationCode":"if (key != null && key.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal))\n    key = key.Substring(Constants.CredentialProtocol.GcmStatePrefix.Length);","typeGuard":"static string StripStatePrefix(string k) =>\n    k != null && k.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal)\n        ? k.Substring(Constants.CredentialProtocol.GcmStatePrefix.Length) : k;","tryCatchPattern":"try { response.SetState(key, value); }\ncatch (ArgumentException e) when (e.Message.Contains(\"prefix is reserved\")) { response.SetState(StripStatePrefix(key), value); }","preventionTips":["Always store bare keys; remember the prefix is emit-time only.","When round-tripping emitted state lines, strip 'key=' and the prefix before re-setting.","Centralize state key creation in one helper."],"tags":["csharp","validation","reserved-prefix","git-state-protocol"],"backgroundTag":"invalid-argument-value","analyzedSha":"e8ce762cd04b4100ae637b5fbf39ef9d0a96561e","analyzedAt":"2026-09-11T17:15:08.753Z","contentChangedAt":"2026-09-11T17:15:08.753Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}