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

helper error ( )

Error message

helper error ({exitCode}): {errorMessage}

What it means

After InvokeHelperAsync runs the UI helper, it parses the helper's JSON stdout and exit code. A non-zero exit code means the helper itself failed; GCM throws a Trace2Exception embedding the exit code and the "error" field from the helper's output dict (or "Unknown" if absent).

Solutions

  1. Read the errorMessage in the exception — it carries the helper's own error reason.
  2. Re-run with trace2/GCM tracing enabled (GCM_TRACE=1, GIT_TRACE2) to capture the helper's diagnostics.
  3. If the helper consistently fails, reinstall GCM or fall back to a different credential mode (PAT/basic via config).
Defensive patterns

Strategy: try-catch

Try / catch

try { var dict = await InvokeHelperAsync(...); } catch (Trace2Exception ex) when (ex.Message.StartsWith("helper error (")) { /* log ex.Message (contains exit code + helper error), retry or fall back */ }

Prevention

When it happens

Trigger: The spawned UI helper process exits non-zero with a JSON body containing an "error" key (or none), during any flow routed through InvokeHelperAsync (OAuth, account picker, etc.).

Common situations: Helper crashed or user aborted in an unexpected way; helper failed OAuth token exchange; platform-specific helper bugs; corrupted helper output causing misparse.

Related errors


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

Appendix: source

Thrown at src/Core/Authentication/AuthenticationBase.cs:80

            if (standardInput is not null)
            {
                await standardInput.BaseStream.CopyToAsync(process.StandardInput.BaseStream, ct);
                process.StandardInput.Close();
            }

            IDictionary<string, string> resultDict = await process.StandardOutput.ReadDictionaryAsync(StringComparer.OrdinalIgnoreCase);

            await Task.Run(() => process.WaitForExit(), ct);
            int exitCode = process.ExitCode;

            if (exitCode != 0)
            {
                if (!resultDict.TryGetValue("error", out string errorMessage))
                {
                    errorMessage = "Unknown";
                }

                throw new Trace2Exception(Context.Trace2, $"helper error ({exitCode}): {errorMessage}");
            }

            return resultDict;
        }

        protected void ThrowIfUserInteractionDisabled()
        {
            if (!Context.Settings.IsInteractionAllowed)
            {
                string envName = Constants.EnvironmentVariables.GcmInteractive;
                string cfgName = string.Format("{0}.{1}",
                    Constants.GitConfiguration.Credential.SectionName,
                    Constants.GitConfiguration.Credential.Interactive);

                Context.Trace.WriteLine($"{envName} / {cfgName} is false/never; user interactivity has been disabled.");
                throw new Trace2InvalidOperationException(Context.Trace2, "Cannot prompt because user interactivity has been disabled.");
            }
        }

View on GitHub (pinned to e8ce762cd0)