{"record":{"id":"7da5da86e8edd11b","repo":"vectordotdev/vector","slug":"invalid-fragment-filename-filename-extension-must-be","errorCode":null,"errorMessage":"invalid fragment filename '{filename}': extension must be markdown (.md).","messagePattern":"invalid fragment filename '(.+?)': extension must be markdown \\(\\.md\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/check/changelog_fragments.rs","lineNumber":185,"sourceCode":"    let parts: Vec<&str> = filename.split('.').collect();\n    if parts.len() != 3 {\n        bail!(\n            \"invalid fragment filename '{filename}': expected '<unique_name>.<fragment_type>.md'\"\n        );\n    }\n    let fragment_type = parts[1];\n    let Some(known) = FRAGMENT_TYPES.iter().find(|t| t.name == fragment_type) else {\n        bail!(\n            \"invalid fragment filename '{filename}': fragment type must be one of ({}).\",\n            FRAGMENT_TYPES\n                .iter()\n                .map(|t| t.name)\n                .collect::<Vec<_>>()\n                .join(\"|\")\n        );\n    };\n    if parts[2] != \"md\" {\n        bail!(\"invalid fragment filename '{filename}': extension must be markdown (.md).\");\n    }\n    Ok(known)\n}\n\nfn validate_contents(\n    path: &std::path::Path,\n    filename: &str,\n    fragment_type: &FragmentType,\n) -> Result<()> {\n    let content = std::fs::read_to_string(path)?;\n    // Match generate_cue.rs, which reads `lines().last()` verbatim: the authors\n    // line must be the last line, no trailing blank lines allowed.\n    let last_line = content.lines().last().unwrap_or(\"\");\n\n    let Some(names) = last_line.strip_prefix(\"authors: \") else {\n        bail!(\n            \"invalid fragment contents for '{filename}': last line must be 'authors: <name> [<name> ...]' (no trailing blank lines).\"\n        );","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/check/changelog_fragments.rs#L167-L203","documentation":"Fired by validate_filename when a changelog fragment file's extension is not '.md'. The checker splits the filename on '.' and requires exactly three parts '<unique_name>.<fragment_type>.md'; a file with a different extension (e.g. 'foo.breaking.txt' or editor backups) fails this shape check and is rejected as an invalid fragment filename.","triggerScenarios":"Running `vdev check changelog-fragments` with files like `jdoe.new-feature.mdx`, `jdoe.new-feature.markdown`, or `jdoe.new-feature.md.orig` (parts.len()==3 but parts[2] != \"md\").","commonSituations":"Authoring in MDX; saving with a text editor that appends an extension; backup tools appending suffixes like `.orig` in changelog.d/.","solutions":["Rename the file so it ends in `.md`.","Move non-Markdown notes out of the changelog.d directory.","Delete leftover editor backup files from changelog.d/."],"exampleFix":"// before\nchangelog.d/jdoe.new-feature.markdown\n// after\nchangelog.d/jdoe.new-feature.md","handlingStrategy":"validation","validationCode":"let name = \"jdoe.new-feature.markdown\";\nassert_eq!(name.split('.').last().unwrap(), \"md\", \"fragment must end in .md\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only write fragments as plain Markdown with a .md extension.","Avoid MDX or other Markdown variants in changelog.d/.","Exclude changelog.d/ from editor backup plugins."],"tags":["rust","changelog","naming","ci"],"backgroundTag":"invalid-argument-format","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}