git-ecosystem/git-credential-manager · error · Trace2Exception

Missing 'pat' in response

Error message

Missing 'pat' in response

What it means

Thrown in GetAuthenticationViaHelperAsync when the GitLab helper response declares `mode=pat` but the result dictionary contains no 'pat' key. A PAT-mode response is required to carry the personal access token value, so the missing field is a protocol violation.

Solutions

  1. Fix the helper to only emit `mode=pat` together with a non-empty `pat=` line.
  2. Verify the token source the helper reads (env var, file, CLI) is actually populated before invoking git.
  3. Switch to GCM's built-in GitLab authentication (remove the custom helper override) if the custom helper cannot be maintained.

Example fix

// before
if [ -z "$TOKEN" ]; then echo "mode=pat"; exit 0; fi
echo "mode=pat"
echo "pat=$TOKEN"
// after
echo "mode=pat"
echo "pat=$TOKEN"
Defensive patterns

Strategy: validation

Validate before calling

// Ensure token is present before emitting pat mode
if [ -z "$GITLAB_PAT" ]; then echo "Error: GITLAB_PAT not set" >&2; exit 1; fi
echo "mode=pat"
echo "pat=$GITLAB_PAT"

Try / catch

try {
  var result = await auth.GetAuthenticationAsync(...);
} catch (Trace2Exception ex) when (ex.Message.Contains("Missing 'pat' in response")) {
  // helper is broken; prompt for PAT manually
  var pat = Console.ReadLine();
}

Prevention

When it happens

Trigger: Helper returns mode=pat but omits the pat= line - e.g. a script that failed to read the token but still printed the mode, or a partially-implemented custom helper.

Common situations: Custom helper scripts whose token retrieval failed silently; helpers reading tokens from files/env vars that are empty or unset; truncated helper output due to pipe errors.

Related errors


AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11). Data as JSON: /api/errors/b195ec2d49835c16. Report an issue: GitHub.

Appendix: source

Thrown at src/GitLab/GitLabAuthentication.cs:227

            promptArgs.AppendFormat(" --url {0}", QuoteCmdArg(targetUri.ToString()));

            if ((modes & AuthenticationModes.Basic) != 0) promptArgs.Append(" --basic");
            if ((modes & AuthenticationModes.Browser) != 0) promptArgs.Append(" --browser");
            if ((modes & AuthenticationModes.Pat) != 0) promptArgs.Append(" --pat");

            IDictionary<string, string> resultDict = await InvokeHelperAsync(helperCommand, promptArgs.ToString());

            if (!resultDict.TryGetValue("mode", out string responseMode))
            {
                throw new Trace2Exception(Context.Trace2, "Missing 'mode' in response");
            }

            switch (responseMode.ToLowerInvariant())
            {
                case "pat":
                    if (!resultDict.TryGetValue("pat", out string pat))
                    {
                        throw new Trace2Exception(Context.Trace2, "Missing 'pat' in response");
                    }

                    if (!resultDict.TryGetValue("username", out string patUserName))
                    {
                        // Username is optional for PATs
                    }

                    return new AuthenticationPromptResult(
                        AuthenticationModes.Pat, new GitCredential(patUserName, pat));

                case "browser":
                    return new AuthenticationPromptResult(AuthenticationModes.Browser);

                case "basic":
                    if (!resultDict.TryGetValue("username", out userName))
                    {
                        throw new Trace2Exception(Context.Trace2, "Missing 'username' in response");
                    }

View on GitHub (pinned to e8ce762cd0)