{"record":{"id":"6e627ea99d08d436","repo":"SubtitleEdit/subtitleedit","slug":"custom-text-output-requires-custom-format-path","errorCode":null,"errorMessage":"Custom text output requires --custom-format=<path-to-template.xml>.","messagePattern":"Custom text output requires --custom-format=<path-to-template\\.xml>\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/seconv/Core/LibSEIntegration.cs","lineNumber":377,"sourceCode":"        string formatName,\n        string? encodingName = null,\n        SubtitleFormat? sourceFormat = null,\n        int? pacCodePage = null,\n        string? ebuHeaderFile = null,\n        ConversionOptions? options = null)\n    {\n        if (subtitle == null)\n        {\n            throw new ArgumentNullException(nameof(subtitle));\n        }\n\n        // Custom text format — render via UiLogic CustomTextFormatter\n        if (formatName.Replace(\" \", \"\").Equals(\"CustomTextFormat\", StringComparison.OrdinalIgnoreCase) ||\n            formatName.Replace(\" \", \"\").Equals(\"CustomText\", StringComparison.OrdinalIgnoreCase))\n        {\n            if (options is null || string.IsNullOrWhiteSpace(options.CustomFormatFile))\n            {\n                throw new InvalidOperationException(\"Custom text output requires --custom-format=<path-to-template.xml>.\");\n            }\n            var template = CustomFormatTemplateLoader.Load(options.CustomFormatFile);\n            var rendered = Nikse.SubtitleEdit.UiLogic.Export.CustomTextFormatter.GenerateCustomText(\n                template, subtitle.Paragraphs.ToList(), Path.GetFileNameWithoutExtension(filePath), string.Empty);\n            var outputDirCustom = Path.GetDirectoryName(filePath);\n            if (!string.IsNullOrEmpty(outputDirCustom) && !Directory.Exists(outputDirCustom))\n            {\n                Directory.CreateDirectory(outputDirCustom);\n            }\n            File.WriteAllText(filePath, rendered, new UTF8Encoding(true));\n            return;\n        }\n\n        // Plain text output — strip HTML; optional merge/unbreak and blank-line control\n        if (formatName.Replace(\" \", \"\").Equals(\"PlainText\", StringComparison.OrdinalIgnoreCase))\n        {\n            var outputDirPlain = Path.GetDirectoryName(filePath);\n            if (!string.IsNullOrEmpty(outputDirPlain) && !Directory.Exists(outputDirPlain))","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/seconv/Core/LibSEIntegration.cs#L359-L395","documentation":"Thrown when the target output format is 'Custom Text' / 'CustomTextFormat' but no template file was supplied via ConversionOptions.CustomFormatFile. Custom text output renders paragraphs through Nikse.SubtitleEdit's CustomTextFormatter, which requires an XML template defining the header, per-cue, and footer markup — without it there is nothing to render.","triggerScenarios":"Calling the save/export path with a formatName matching 'Custom Text' or 'CustomTextFormat' while options is null or options.CustomFormatFile is null/blank. Typically a CLI invocation where --custom-format was omitted.","commonSituations":"Forgetting to pass --custom-format=<template.xml> on the command line when the output format is custom text; specifying the template path with a typo so it reads as blank; programmatic use where ConversionOptions was constructed without setting CustomFormatFile.","solutions":["Pass --custom-format=<path-to-template.xml> on the CLI when the output format is Custom Text.","In programmatic use, set options.CustomFormatFile to a valid template path before calling Save.","If you don't have a template, export to a standard format (SRT, ASS, VTT) instead."],"exampleFix":"// before\nvar options = new ConversionOptions();\nLibSEIntegration.SaveSubtitle(sub, outPath, \"Custom Text\", options);\n\n// after\nvar options = new ConversionOptions { CustomFormatFile = \"my-template.xml\" };\nLibSEIntegration.SaveSubtitle(sub, outPath, \"Custom Text\", options);","handlingStrategy":"validation","validationCode":"if (formatName.Replace(\" \", \"\").Equals(\"CustomTextFormat\", StringComparison.OrdinalIgnoreCase))\n{\n    if (options == null || string.IsNullOrWhiteSpace(options.CustomFormatFile))\n        throw new InvalidOperationException(\"Custom text output requires a template file path.\");\n}","typeGuard":"static bool NeedsCustomTemplate(string formatName) => formatName.Replace(\" \", \"\").Equals(\"CustomTextFormat\", StringComparison.OrdinalIgnoreCase) || formatName.Replace(\" \", \"\").Equals(\"CustomText\", StringComparison.OrdinalIgnoreCase);","tryCatchPattern":"try { LibSEIntegration.SaveSubtitle(sub, outPath, \"Custom Text\", options); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Custom text output requires\"))\n{ /* prompt user to supply --custom-format=<template.xml> */ }","preventionTips":["Always set ConversionOptions.CustomFormatFile when the output format is Custom Text.","Validate that the template file exists and is a valid XML template before export.","In CLI argument parsing, require --custom-format whenever the format is custom text."],"tags":["custom-format","missing-config","validation","subtitles"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}