{"record":{"id":"576f3fa86fba9a95","repo":"Textualize/textual","slug":"template-must-contain-at-least-one-non-separator-c","errorCode":null,"errorMessage":"Template must contain at least one non-separator character","messagePattern":"Template must contain at least one non-separator character","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/textual/widgets/_masked_input.py","lineNumber":141,"sourceCode":"                    char_definition = self.CharDefinition(\n                        re.compile(pattern), char_flags\n                    )\n                else:\n                    char_definition = self.CharDefinition(\n                        re.compile(re.escape(char)), _CharFlags.SEPARATOR, char\n                    )\n\n            char_definition.flags |= flags\n            self.template.append(char_definition)\n\n        if template_chars:\n            self.blank = template_chars[0]\n\n        if all(\n            (_CharFlags.SEPARATOR in char_definition.flags)\n            for char_definition in self.template\n        ):\n            raise ValueError(\n                \"Template must contain at least one non-separator character\"\n            )\n\n        self.update_mask(input.placeholder)\n\n    def validate(self, value: str) -> ValidationResult:\n        \"\"\"Checks if `value` matches this template, always returning a ValidationResult.\n\n        Args:\n            value: The string value to be validated.\n\n        Returns:\n            A ValidationResult with the validation outcome.\n\n        \"\"\"\n        if self.check(value.ljust(len(self.template), chr(0)), False):\n            return self.success()\n        else:","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/widgets/_masked_input.py#L123-L159","documentation":"A ValueError raised in MaskedInput.__init__ (via _Template construction) when every character slot in the template is a separator — i.e. there is no position for user input. A mask like '--' or '//' with only separator characters cannot hold a value, so construction fails.","triggerScenarios":"Passing a template consisting solely of separator characters (e.g. MaskedInput(template='///')); a template built dynamically from config where all input slots were dropped; templates like '()' where every char is a literal separator.","commonSituations":"Config-driven masks for phone/date formats where a malformed pattern slips through; building templates by string concatenation that accidentally removes all input slots; typos using a wrong mask character for every slot.","solutions":["Ensure the template contains at least one input character (e.g. '9999-9999', not '-----').","Validate user-supplied templates before constructing MaskedInput (check for at least one non-separator slot).","Provide a sensible default template when validation fails.","Consult MaskedInput docs for valid template characters (1, a, A, 9, *, etc.)."],"exampleFix":"# before\nfield = MaskedInput(template=cfg['mask'])  # cfg['mask'] == '--'\n\n# after\nmask = cfg['mask'] if any(c in '1aA9*@#' for c in cfg['mask']) else '999-9999'\nfield = MaskedInput(template=mask)","handlingStrategy":"validation","validationCode":"INPUT_CHARS = set('1aA9*@#')\nassert any(c in INPUT_CHARS for c in template), 'mask has no input slots'\nMaskedInput(template=template)","typeGuard":null,"tryCatchPattern":"try:\n    field = MaskedInput(template=mask)\nexcept ValueError:\n    field = MaskedInput(template='999-9999')  # fallback mask","preventionTips":["Validate config-supplied masks contain at least one input slot","Default to a known-good mask on invalid input","Refer to MaskedInput docs for valid template characters"],"tags":["textual","masked-input","value-error","template-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}