{"record":{"id":"d74f317f128620b4","repo":"iOfficeAI/OfficeCLI","slug":"progid-progid-exceeds-39-characters-limit-39","errorCode":null,"errorMessage":"progId '{progId}' exceeds 39 characters (limit: 39, actual: {progId.Length}).","messagePattern":"progId '(.+?)' exceeds 39 characters \\(limit: 39, actual: (.+?)\\)\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/OleHelper.cs","lineNumber":492,"sourceCode":"    /// width/height. 2 inches × 0.75 inches matches what Office uses for a\n    /// default \"show as icon\" OLE frame, sized to fit the file-type label.\n    /// </summary>\n    public const long DefaultOleWidthEmu = 1828800;  // 2 inches\n    public const long DefaultOleHeightEmu = 685800;   //  0.75 inches\n\n    /// <summary>\n    /// Validate a COM ProgID string against the well-known Windows COM\n    /// constraints: the identifier must be 1..39 characters long and must\n    /// not start with a digit. OLE spec (MSDN \"ProgID\") is explicit on both\n    /// rules. Handlers previously accepted arbitrary strings silently; this\n    /// method gives users an early, actionable error instead of writing an\n    /// invalid OLE element that Office refuses to open.\n    /// </summary>\n    public static void ValidateProgId(string progId)\n    {\n        if (progId == null) return;\n        if (progId.Length > 39)\n            throw new ArgumentException(\n                $\"progId '{progId}' exceeds 39 characters (limit: 39, actual: {progId.Length}).\");\n        if (progId.Length > 0 && char.IsDigit(progId[0]))\n            throw new ArgumentException(\n                $\"progId '{progId}' cannot start with a digit.\");\n        // COM ProgID character set: letters, digits, '.', '_', '-'. Anything\n        // else (notably XML-unsafe characters like '<', '>', '&', '\"') would\n        // either corrupt the OOXML progId attribute or be rejected by Office\n        // on reopen. Reject early with an actionable error instead of letting\n        // bad bytes land in the package.\n        foreach (var ch in progId)\n        {\n            if (!(char.IsLetterOrDigit(ch) || ch == '.' || ch == '_' || ch == '-'))\n                throw new ArgumentException(\n                    $\"progId '{progId}' contains invalid characters. Only letters, digits, '.', '_', '-' are allowed.\");\n        }\n    }\n\n    /// <summary>","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/OleHelper.cs#L474-L510","documentation":"Thrown by OleHelper.ValidateProgId when a COM ProgID string exceeds 39 characters. The Windows COM specification (MSDN 'ProgID') limits ProgIDs to 1..39 characters. Writing a longer string into the OOXML progId attribute would produce an OLE element that Office refuses to open or activates incorrectly. This is a pre-write validation that gives the user an early, actionable error instead of a corrupted document.","triggerScenarios":"Calling any Add ole or Set ole operation with a 'progId' property longer than 39 characters. For example: 'add ole src=file.pdf progId=MyVeryVeryVeryVeryVeryVeryVeryVeryLongProgIdName' (40+ chars). ValidateProgId is called by ResolveProgId, which is invoked by all three handlers' AddOle/SetOle paths. A null progId passes (returns early), and an empty string passes the length check but may fail the digit-first check.","commonSituations":"A user who manually specifies a progId that is too long. A progId auto-generated from a filename or organization name that exceeds the limit. A copy-paste from a configuration that used a verbose identifier. An adversarial input designed to probe the validation boundary.","solutions":["Shorten the progId to 39 characters or fewer.","Use a standard Office progId like 'Word.Document.12', 'Excel.Sheet.12', 'PowerPoint.Show.12', 'Package', or 'AcroExch.Document' instead of a custom one.","If you need a custom progId, abbreviate it while keeping it meaningful and within the allowed character set."],"exampleFix":"// before — progId too long (40 chars)\nadd ole src=file.pdf progId=MyExtremelyLongCustomProgIdNameHere path='/body'\n\n// after — use standard or shortened progId\nadd ole src=file.pdf progId=Package path='/body'\n// or a shorter custom one\nadd ole src=file.pdf progId=MyApp.Doc path='/body'","handlingStrategy":"validation","validationCode":"// Pre-validate progId length before embedding\nif (progId != null && progId.Length > 39)\n{\n    Console.Error.WriteLine($\"progId '{progId}' is {progId.Length} chars, max is 39.\");\n    progId = progId[..39]; // or use a standard progId like \"Package\"\n}\nOleHelper.ValidateProgId(progId);","typeGuard":null,"tryCatchPattern":"try\n{\n    OleHelper.ValidateProgId(progId);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"exceeds 39 characters\"))\n{\n    // Shorten or switch to a standard progId like \"Package\"\n    progId = \"Package\";\n    OleHelper.ValidateProgId(progId);\n}","preventionTips":["Prefer standard Office progIds (Word.Document.12, Excel.Sheet.12, PowerPoint.Show.12, Package, AcroExch.Document) which are all within the limit.","When generating progIds programmatically, enforce a 39-character maximum.","Let OleHelper.DetectProgId auto-detect the progId from the file extension when you don't need a custom one."],"tags":["ole","progid","com-validation","length-limit","argument-validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}