vectordotdev/vector · error

Invalid filename , no extension

Error message

Invalid filename {}, no extension

What it means

load_and_extract reads a feature-config file and dispatches parsing on the file extension. If Path::extension() returns None (no dot in the filename, or the name ends with a dot), it cannot choose a parser and bails with this message naming the file. The tool only accepts json, toml, yaml, or yml extensions.

Solutions

  1. Rename the file to include a supported extension: .json, .toml, .yaml, or .yml matching its actual content format
  2. If the content is YAML but the file is named e.g. `features`, rename to `features.yaml`
  3. Exclude extension-less files from the input set before calling load_and_extract

Example fix

// before
let feats = load_and_extract(Path::new("config/features"))?;
// after
let feats = load_and_extract(Path::new("config/features.yaml"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_supported_ext(p: &Path) -> bool {
    matches!(p.extension().and_then(|e| e.to_str()).map(|e| e.to_lowercase()).as_deref(),
             Some("json" | "toml" | "yaml" | "yml"))
}

Prevention

When it happens

Trigger: Calling load_and_extract with a Path whose filename has no extension — e.g. "config", "features.", ".gitignore"-style dotfiles whose extension parses as the whole name, or a path ending in a directory separator.

Common situations: Passing a temp file created without a suffix; renaming a config during refactoring and dropping the extension; globbing a directory and feeding every file (including extension-less ones) to the loader.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/c643b4b6c6c5238a. Report an issue: GitHub.

Appendix: source

Thrown at vdev/src/utils/features.rs:121

#[derive(Deserialize)]
struct Component {
    r#type: String,

    #[serde(flatten)]
    options: HashMap<String, Value>,
}

pub fn load_and_extract(filename: &Path) -> Result<Vec<String>> {
    let config = fs::read_to_string(filename)
        .with_context(|| format!("failed to read {}", filename.display()))?;

    let config: FeatureConfig = match filename
        .extension()
        .and_then(OsStr::to_str)
        .map(str::to_lowercase)
        .as_deref()
    {
        None => bail!("Invalid filename {}, no extension", filename.display()),
        Some("json") => serde_json::from_str(&config)?,
        Some("toml") => toml::from_str(&config)?,
        Some("yaml" | "yml") => {
            let value: serde_yaml::Value = serde_yaml::from_str(&config)?;
            if contains_yaml_merge_key(&value) {
                bail!(UNSUPPORTED_YAML_MERGE_KEY);
            }
            serde_yaml::from_value(value)?
        }
        Some(_) => bail!("Invalid filename {}, unknown extension", filename.display()),
    };

    let declared_features = CargoToml::load_from(&find_repo_root()?.join("Cargo.toml"))?
        .features
        .into_keys()
        .collect();

    from_config(&config, &declared_features)

View on GitHub (pinned to bdb87aeaa4)