git-ecosystem/git-credential-manager · error · Exception
Missing section header
Error message
Missing section header
What it means
IniFile.Deserialize parses .gitconfig-style INI content line by line; if a property line (key = value) appears before any [section] header has been seen, 'section' is still null and the parser throws "Missing section header". INI format requires properties to belong to a section, so a leading bare property is unrepresentable.
Solutions
- Open the config file and ensure every property appears under a [section] header — add the missing header at the top
- Check the exact line the parser failed on (it is the first property line before any section)
- Regenerate the config from a known-good template rather than hand-editing
- If constructing INI text programmatically, always emit "[section]\n" before any key=value pairs
Example fix
// before (file content) // name = value // // after // [user] // name = value
Defensive patterns
Strategy: try-catch
Validate before calling
var text = File.ReadAllText(path);
var firstNonEmpty = text.Split('\n').FirstOrDefault(l => !string.IsNullOrWhiteSpace(l)) ?? "";
if (!firstNonEmpty.TrimStart().StartsWith("["))
throw new InvalidDataException($"{path}: first content line must be a [section] header"); Type guard
bool HasSectionHeader(string iniText) => iniText
.Split('\n')
.Any(l => l.TrimStart().StartsWith("[")) &&
!string.IsNullOrWhiteSpace(iniText.Split('\n').FirstOrDefault(l => !string.IsNullOrWhiteSpace(l))) &&
iniText.Split('\n').First(l => !string.IsNullOrWhiteSpace(l)).TrimStart().StartsWith("["); Try / catch
try
{
var ini = IniFile.Deserialize(path, fileSystem);
}
catch (Exception ex) when (ex.Message == "Missing section header")
{
logger.LogError(ex, "{Path} is malformed: a key=value appears before any [section] header", path);
} Prevention
- Never hand-edit git config files without preserving [section] headers
- Validate config files parse (round-trip via IniFile) after automated edits
- When generating INI text, always emit the section header before key=value lines
When it happens
Trigger: Deserializing config text/file where a property line occurs before the first [section] line — e.g. a file starting with "name = value", corrupted config missing its first header, or a truncated file.
Common situations: Hand-edited or machine-generated .gitconfig files missing the initial [core]/[user] header, config files concatenated incorrectly, or templates where the section header line was deleted.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/69027456db9cbd3a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Core/IniFile.cs:171
continue;
}
if (!iniFile.TryGetSection(mainName, subName, out section))
{
var sectionName = new IniSectionName(mainName, subName);
section = new IniSection(sectionName);
iniFile.Sections[sectionName] = section;
}
continue;
}
match = PropertyRegex.Match(line);
if (match.Success)
{
if (section is null)
{
throw new Exception("Missing section header");
}
string propName = match.Groups["name"].Value;
string propValue = match.Groups["value"].Value.Trim();
// Trim trailing comments
int firstDQuote = propValue.IndexOf('"');
int lastDQuote = propValue.LastIndexOf('"');
int commentIdx = propValue.LastIndexOf('#');
if (commentIdx > -1)
{
bool insideDQuotes = firstDQuote > -1 && lastDQuote > -1 &&
(firstDQuote < commentIdx && commentIdx < lastDQuote);
if (!insideDQuotes)
{
propValue = propValue.Substring(0, commentIdx).Trim();
}View on GitHub (pinned to e8ce762cd0)