{"record":{"id":"8569b46c4934bb51","repo":"oraios/serena","slug":"unhandled-comment-normalisation-comment-normalis","errorCode":null,"errorMessage":"Unhandled comment normalisation: {comment_normalisation}","messagePattern":"Unhandled comment normalisation: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/serena/util/yaml.py","lineNumber":209,"sourceCode":"                                # and adding an empty line at the beginning instead\n                                if preceding_comment is not None and yaml_comment_entry_is_empty(\n                                    preceding_comment[ITEM_COMMENT_INDEX_BEFORE]\n                                ):\n                                    last_token.value = last_token.value[:-1]\n\n                                    first_token = token_list[0]\n                                    if isinstance(first_token, CommentToken):\n                                        if not first_token.value.startswith(\"\\n\"):\n                                            first_token.value = \"\\n\" + first_token.value\n\n                                    preceding_comment[ITEM_COMMENT_INDEX_BEFORE] = token_list\n                                    current_comment[ITEM_COMMENT_INDEX_BEFORE] = None\n                    preceding_comment = current_comment\n\n            # remove nested comments, as we assume that only top-level keys are supposed to be commented\n            remove_nested_comments()\n        case _:\n            raise ValueError(f\"Unhandled comment normalisation: {comment_normalisation}\")\n\n\ndef save_yaml(path: str, data: dict | CommentedMap, preserve_comments: bool = True) -> None:\n    yaml = _create_yaml(preserve_comments)\n    target_dir = os.path.dirname(path)\n    os.makedirs(target_dir, exist_ok=True)\n    # Atomic write: dump to a temp file in the SAME directory, then os.replace onto the target.\n    # A plain truncate-and-write (open(path, \"w\")) is NOT atomic: a concurrent writer (e.g. a second\n    # Serena process updating the auto-managed registered-projects list) or an interrupted write can\n    # leave the file half-overwritten — writing a shorter value over a longer one leaves a stale tail,\n    # which corrupts the YAML so it no longer parses and every later load fails. temp + os.replace makes\n    # each write all-or-nothing (last-writer-wins, never a corrupt interleave).\n    fd, tmp = tempfile.mkstemp(dir=target_dir, prefix=os.path.basename(path) + \".\", suffix=\".tmp\")\n    try:\n        with os.fdopen(fd, \"w\", encoding=SERENA_FILE_ENCODING) as f:\n            yaml.dump(data, f)\n        _replace_with_retry(tmp, path)\n    except BaseException:","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/util/yaml.py#L191-L227","documentation":"normalise_yaml_comments dispatches on a YamlCommentNormalisation enum value; the `case _:` branch raises ValueError when the supplied value is not one of the handled normalisation modes. Since the value is expected to be an enum member, hitting this indicates a non-enum or out-of-range value was passed.","triggerScenarios":"Calling normalise_yaml_comments (public) with a comment_normalisation argument that is not a member of YamlCommentNormalisation — e.g. a raw string like 'leading', an int, or an enum from a different/older version.","commonSituations":"Programmatic config manipulation passing a string instead of the enum; version drift where an enum value was renamed or removed between releases.","solutions":["Import YamlCommentNormalisation from serena.util.yaml and pass an enum member (e.g. YamlCommentNormalisation.LEADING), not a string.","Check the enum definition for the exact accepted values in the installed version.","Audit call sites for values loaded from external config/user input that bypass the enum."],"exampleFix":"// before\nnormalise_yaml_comments(data, 'leading')\n// after\nfrom serena.util.yaml import YamlCommentNormalisation\nnormalise_yaml_comments(data, YamlCommentNormalisation.LEADING)","handlingStrategy":"validation","validationCode":"from serena.util.yaml import YamlCommentNormalisation\n\ndef assert_valid_normalisation(mode) -> None:\n    if not isinstance(mode, YamlCommentNormalisation):\n        raise TypeError(f\"comment_normalisation must be a YamlCommentNormalisation, got {type(mode).__name__}\")","typeGuard":"def is_comment_normalisation(value) -> bool:\n    return isinstance(value, YamlCommentNormalisation)","tryCatchPattern":"try:\n    normalise_yaml_comments(data, mode)\nexcept ValueError as e:\n    if str(e).startswith('Unhandled comment normalisation'):\n        mode = YamlCommentNormalisation(mode)  # coerce strings if possible\n        normalise_yaml_comments(data, mode)\n    else:\n        raise","preventionTips":["Always pass enum members, never raw strings, for comment_normalisation.","Use typing.Literal[YamlCommentNormalisation] annotations on wrapper functions.","When upgrading the library, grep for YamlCommentNormalisation usages against the current enum."],"tags":["yaml","enum","arguments"],"backgroundTag":"invalid-enum-value","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}