git-ecosystem/git-credential-manager · error · ArgumentException
State key cannot start with
Error message
State key cannot start with '{GcmStatePrefix}'; the prefix is reserved and added automatically when state is emitted. What it means
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.
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.
Example fix
// before
response.SetState("gcm-oauth-round", "1");
// after
response.SetState("oauth-round", "1"); // prefix added automatically on emit Defensive patterns
Strategy: validation
Validate before calling
if (key != null && key.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal))
key = key.Substring(Constants.CredentialProtocol.GcmStatePrefix.Length); Type guard
static string StripStatePrefix(string k) =>
k != null && k.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal)
? k.Substring(Constants.CredentialProtocol.GcmStatePrefix.Length) : k; Try / catch
try { response.SetState(key, value); }
catch (ArgumentException e) when (e.Message.Contains("prefix is reserved")) { response.SetState(StripStatePrefix(key), value); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- State key cannot be null or whitespace.
- State key cannot contain '=', newline, or NUL characters.
- State value cannot contain newline or NUL characters.
- State value cannot be null.
- Must specify at least one AuthenticationModes
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/fd47bf6bb1bd42da.
Report an issue: GitHub.
Appendix: source
Thrown at src/Core/GitStateValidation.cs:98
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentException("State key cannot be null or whitespace.", nameof(key));
}
foreach (char c in key)
{
if (c == EQ || c == LF || c == NUL)
{
throw new ArgumentException(
"State key cannot contain '=', newline, or NUL characters.",
nameof(key));
}
}
if (key.StartsWith(Constants.CredentialProtocol.GcmStatePrefix, StringComparison.Ordinal))
{
throw new ArgumentException(
$"State key cannot start with '{Constants.CredentialProtocol.GcmStatePrefix}'; " +
"the prefix is reserved and added automatically when state is emitted.",
nameof(key));
}
}
/// <summary>
/// Throws <see cref="ArgumentException"/> if <paramref name="value"/> is
/// not a legal state entry value.
/// </summary>
public static void ValidateValue(string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value), "State value cannot be null.");
}
foreach (char c in value)View on GitHub (pinned to e8ce762cd0)