{"record":{"id":"b1385ff90a7d143d","repo":"stride3d/stride","slug":"could-not-parse-a-valid-version-from-sourcefile-majorminor","errorCode":null,"errorMessage":"Could not parse a valid version from {sourceFile} (MajorMinor='{majorMinor}', Patch='{patch}'). Check the 'MajorMinor = \"x.y\";' and 'Patch = \"n\";' lines.","messagePattern":"Could not parse a valid version from (.+?) \\(MajorMinor='(.+?)', Patch='(.+?)'\\)\\. Check the 'MajorMinor = \"x\\.y\";' and 'Patch = \"n\";' lines\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"sources/targets/StrideVersionTasks.cs","lineNumber":35,"sourceCode":"using System.Threading;\nusing Microsoft.Build.Framework;\nusing Microsoft.Build.Utilities;\n\n// Shared version logic for both the dev and release generators.\ninternal static class StrideVersionUtil\n{\n    // Reads the editable inputs from SharedAssemblyInfo.cs. The version is composed MajorMinor.Patch (Patch is the\n    // only patch source; MajorMinor the only major.minor source — no duplication to drift).\n    public static void ReadInputs(string sourceFile, out string majorMinor, out string version, out string suffix)\n    {\n        string data = File.ReadAllText(sourceFile);\n        majorMinor = Field(data, \"MajorMinor\");\n        string patch = Field(data, \"Patch\");\n        suffix = Field(data, \"NuGetVersionSuffix\");\n        // Fail loudly on an unparseable version rather than emit a malformed one (e.g. \".\" or \"4.4.\") that would\n        // ship a weird package or compile a garbage const. Field returns \"\" when a line's shape changed.\n        if (!Regex.IsMatch(majorMinor, @\"^[0-9]+\\.[0-9]+$\") || !Regex.IsMatch(patch, @\"^[0-9]+$\"))\n            throw new Exception(\"Could not parse a valid version from \" + sourceFile + \" (MajorMinor='\" + majorMinor +\n                \"', Patch='\" + patch + \"'). Check the 'MajorMinor = \\\"x.y\\\";' and 'Patch = \\\"n\\\";' lines.\");\n        version = majorMinor + \".\" + patch;\n    }\n\n    private static string Field(string data, string name)\n        => Regex.Match(data, name + \" = \\\"([^\\\"]*)\\\";\").Groups[1].Value;\n\n    public static string Git(string workingDir, string args)\n    {\n        var psi = new ProcessStartInfo(\"git\", args)\n        {\n            WorkingDirectory = workingDir,\n            RedirectStandardOutput = true,\n            UseShellExecute = false\n        };\n        using (var p = Process.Start(psi))\n        {\n            string output = p.StandardOutput.ReadToEnd().Trim();","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/targets/StrideVersionTasks.cs#L17-L53","documentation":"StrideVersionTasks.ReadInputs parses version fields (MajorMinor, Patch, NuGetVersionSuffix) out of a source file via regex and validates them before building the composed version string. If MajorMinor does not match 'x.y' or Patch is not a number, it throws with the parsed values and the file path, so a malformed version is never silently propagated into packages or compiled constants.","triggerScenarios":"Calling ReadInputs when the version source file's 'MajorMinor = \"x.y\";' or 'Patch = \"n\";' lines are missing, renamed, commented out, reformatted (e.g. extra spaces, single quotes), or contain placeholders/empty strings — Field() then returns \"\" and the regex checks fail.","commonSituations":"Manual edits to the version file during release prep; branch merges dropping the version block; CI checking out a file where version props are injected by another tool that didn't run; accidental change of assignment style (\"=\" spacing or semicolon removed).","solutions":["Open the file named in the message and fix the MajorMinor/Patch lines to the exact shapes MajorMinor = \"4.2\"; and Patch = \"1\";.","Restore the version file from git (git checkout -- <file>) if the lines were accidentally changed or deleted.","Re-run whatever tool injects version properties into the file before invoking ReadInputs.","Check for merge-conflict markers or commented-out lines inside the version file."],"exampleFix":"// before (StrideVersion.props)\n<PropertyGroup><MajorMinor>4</MajorMinor><Patch></Patch></PropertyGroup>\n// after\n// MajorMinor = \"4.2\";\n// Patch = \"1\";","handlingStrategy":"validation","validationCode":"static void ValidateVersionFile(string path)\n{\n    var data = File.ReadAllText(path);\n    var mm = Regex.Match(data, \"MajorMinor = \\\"([^\\\"]*)\\\";\").Groups[1].Value;\n    var patch = Regex.Match(data, \"Patch = \\\"([^\\\"]*)\\\";\").Groups[1].Value;\n    if (!Regex.IsMatch(mm, @\"^[0-9]+\\.[0-9]+$\") || !Regex.IsMatch(patch, @\"^[0-9]+$\"))\n        throw new InvalidOperationException($\"{path} has invalid MajorMinor='{mm}' or Patch='{patch}'.\");\n}","typeGuard":"static bool IsWellFormedVersion(string majorMinor, string patch) =>\n    Regex.IsMatch(majorMinor ?? \"\", @\"^[0-9]+\\.[0-9]+$\") &&\n    Regex.IsMatch(patch ?? \"\", @\"^[0-9]+$\");","tryCatchPattern":"try\n{\n    versionTasks.ReadInputs();\n}\ncatch (Exception ex) when (ex.Message.Contains(\"Could not parse a valid version from\"))\n{\n    logger.Error($\"Version source file malformed: {ex.Message}. Restore MajorMinor/Patch lines.\");\n    throw;\n}","preventionTips":["Keep the version file in the exact 'Name = \"value\";' line format and never hand-edit it mid-release.","Add a schema/lint check of the version file to CI before build tasks run.","Resolve merge conflicts in the version file by regenerating it, not hand-merging."],"tags":["versioning","parsing","build"],"backgroundTag":"invalid-argument-format","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}