{"record":{"id":"4302c576e0df7f91","repo":"OrchardCMS/OrchardCore","slug":"content-type-name-must-start-with-a-letter","errorCode":null,"errorMessage":"Content type name must start with a letter","messagePattern":"Content type name must start with a letter","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Metadata/Builders/ContentTypeDefinitionBuilder.cs","lineNumber":43,"sourceCode":"        if (existing == null)\n        {\n            _parts = [];\n            _settings = [];\n        }\n        else\n        {\n            _name = existing.Name;\n            _displayName = existing.DisplayName;\n            _parts = existing.Parts.ToList();\n            _settings = existing.Settings.Clone();\n        }\n    }\n\n    public ContentTypeDefinition Build()\n    {\n        if (!char.IsLetter(_name[0]))\n        {\n            throw new ArgumentException(\"Content type name must start with a letter\", \"name\");\n        }\n        if (!string.Equals(_name, _name.ToSafeName(), StringComparison.OrdinalIgnoreCase))\n        {\n            throw new ArgumentException(\"Content type name contains invalid characters\", \"name\");\n        }\n        if (_name.IsReservedContentName())\n        {\n            throw new ArgumentException(\"Content type name is reserved for internal use\", \"name\");\n        }\n\n        return new ContentTypeDefinition(_name, _displayName, _parts, _settings);\n    }\n\n    [Obsolete(\"This method has been deprecated, please use WithName() instead.\")]\n    public ContentTypeDefinitionBuilder Named(string name) => WithName(name);\n\n    public ContentTypeDefinitionBuilder WithName(string name)\n    {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Metadata/Builders/ContentTypeDefinitionBuilder.cs#L25-L61","documentation":"ContentTypeDefinitionBuilder.Build throws ArgumentException when the content type name's first character is not a letter. Type names are used as safe identifiers across indexes, routing and display binding.","triggerScenarios":"builder.Named(\"1Product\") / Named(\"\") then Build(); AlterTypeDefinitionAsync migrations generating type names from data starting with digits; null/empty name.","commonSituations":"Type names derived from database table or entity names with numeric prefixes, recipes with invalid type names, programmatic type creation from external catalogs.","solutions":["Prefix the type name with a letter, e.g. \"Product2024\" instead of \"2024Product\".","Validate before building: char.IsLetter(name[0]).","Ensure the name is non-null and non-empty before Build()."],"exampleFix":"// before\nbuilder.Named(\"2FACode\").Build(); // throws\n\n// after\nbuilder.Named(\"Code2FA\").Build();","handlingStrategy":"validation","validationCode":"bool IsValidTypeName(string name) =>\n    !string.IsNullOrEmpty(name) && char.IsLetter(name[0]);","typeGuard":null,"tryCatchPattern":"try\n{\n    var typeDef = builder.Build();\n}\ncatch (ArgumentException e)\n{\n    _logger.LogError(e, \"Invalid content type name '{Name}'\", name);\n}","preventionTips":["Validate type names before AlterTypeDefinitionAsync in migrations.","Prefix generated type names with a letter when sources start with digits.","Add recipes/migration tests covering type name edge cases."],"tags":["naming","content-type","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}