{"record":{"id":"0b66349dc102dfcb","repo":"Humanizr/Humanizer","slug":"path-must-use-the-explicit-inventory-reason","errorCode":null,"errorMessage":"'{path}' must use the explicit inventory, reason, sources, and provenance contract in a release locale.","messagePattern":"'(.+?)' must use the explicit inventory, reason, sources, and provenance contract in a release locale\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs","lineNumber":195,"sourceCode":"\n            var unsupportedPaths = unsupportedPathBuilder\n                .OrderBy(static path => path, StringComparer.Ordinal)\n                .ToArray();\n            if (unsupportedPaths.Length > 0)\n            {\n                throw new InvalidOperationException(\n                    $\"Release duration-case completeness requires zero unsupported dispositions; found {unsupportedPaths.Length}: {string.Join(\", \", unsupportedPaths)}.\");\n            }\n\n            return new DurationCaseCoverageInput(immutableCatalogs, rows.MoveToImmutable());\n        }\n\n        internal static void ValidateReleaseCompleteness(DurationCaseCatalog catalog)\n        {\n            var path = $\"{catalog.LocaleCode}.durationCases\";\n            if (catalog.Sources.IsEmpty)\n            {\n                throw new InvalidOperationException(\n                    $\"'{path}' must use the explicit inventory, reason, sources, and provenance contract in a release locale.\");\n            }\n\n            var unsupportedPaths = GetUnsupportedPaths(catalog);\n            if (unsupportedPaths.Length > 0)\n            {\n                throw new InvalidOperationException(\n                    $\"Release duration-case completeness requires zero unsupported dispositions; found {unsupportedPaths.Length}: {string.Join(\", \", unsupportedPaths)}.\");\n            }\n        }\n\n        static ImmutableArray<string> GetUnsupportedPaths(DurationCaseCatalog catalog)\n        {\n            var paths = ImmutableArray.CreateBuilder<string>();\n            var path = $\"{catalog.LocaleCode}.durationCases\";\n            if (catalog.Classification == DurationCaseClassification.Unsupported)\n            {\n                paths.Add($\"{path}.classification\");","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs#L177-L213","documentation":"Thrown by ValidateReleaseCompleteness (the single-catalog variant) when a DurationCaseCatalog has empty Sources. This is the per-catalog check used in tests and targeted validation. It enforces the same provenance contract as the batch check (error 48) but for one catalog at a time.","triggerScenarios":"ValidateReleaseCompleteness checks catalog.Sources.IsEmpty and throws if true. Called from tests via HumanizerSourceGenerator.DurationCaseCoverageInput.ValidateReleaseCompleteness(catalog) to verify individual locale catalogs meet release standards.","commonSituations":"A test constructs a DurationCaseCatalog via the legacy parse path (ParseLegacyForTests) and then passes it to ValidateReleaseCompleteness. The legacy path produces empty sources, so the validation fails. Also used to check a specific locale before the full release build.","solutions":["Parse the locale via the explicit path (ParseExplicit) which requires sources, rather than ParseLegacyForTests.","Ensure the durationCases YAML has a 'sources' key with at least one source entry.","In tests, only call ValidateReleaseCompleteness on catalogs built through the explicit provenance path."],"exampleFix":"// before (test using legacy parse then release validation)\nvar catalog = DurationCaseNormalization.ParseLegacyForTests(\"xx\", value);\nDurationCaseCoverageInput.ValidateReleaseCompleteness(catalog);\n// after (test using explicit parse with sources)\nvar catalog = DurationCaseNormalization.ParseForTests(\"xx\", explicitYamlWithSources);\nDurationCaseCoverageInput.ValidateReleaseCompleteness(catalog);","handlingStrategy":"validation","validationCode":"// Single-catalog pre-check for sources before calling ValidateReleaseCompleteness\nstatic bool CatalogHasSources(DurationCaseCatalog catalog) => !catalog.Sources.IsEmpty;","typeGuard":null,"tryCatchPattern":"// In tests, guard the release check\nif (!catalog.Sources.IsEmpty)\n{\n    DurationCaseCoverageInput.ValidateReleaseCompleteness(catalog);\n}","preventionTips":["Only call ValidateReleaseCompleteness on catalogs built through the explicit parse path.","Legacy-parsed catalogs will always have empty sources — don't pass them to release checks.","In tests, assert the expected exception if deliberately testing a non-release scenario."],"tags":["source-generator","duration-cases","release-gate","provenance","testing"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}