nikivdev/code · error · anyhow::Error

pass either a subcommand or a bare import path, not both

Error message

pass either a subcommand or a bare import path, not both

What it means

The `f ext` dispatcher supports two mutually exclusive invocation styles: a subcommand (list/doctor/enable/disable/init/import) or a bare import path argument. Passing both in one command is ambiguous, so `run` bails immediately with this usage error before executing anything.

Source

Thrown at src/ext.rs:22

use std::process::Command;

use crate::cli::{ExtAction, ExtCommand};
use crate::code;
use crate::config;
use crate::flow_config;
use crate::setup::add_gitignore_entry;
use anyhow::{Context, Result, bail};

pub fn run(cmd: ExtCommand) -> Result<()> {
    match (cmd.action, cmd.path) {
        (Some(ExtAction::List { json }), None) => list_extensions(json),
        (Some(ExtAction::Doctor { json }), None) => doctor_extensions(json),
        (Some(ExtAction::Enable { name }), None) => enable_extension(&name),
        (Some(ExtAction::Disable { name }), None) => disable_extension(&name),
        (Some(ExtAction::Init { name, force }), None) => init_extension(&name, force),
        (Some(ExtAction::Import { path }), None) => import_external_path(&path),
        (None, Some(path)) => import_external_path(&path),
        (Some(_), Some(_)) => bail!("pass either a subcommand or a bare import path, not both"),
        (None, None) => list_extensions(false),
    }
}

fn list_extensions(as_json: bool) -> Result<()> {
    let extensions = flow_config::discover_extensions()?;
    if as_json {
        println!("{}", serde_json::to_string_pretty(&extensions)?);
        return Ok(());
    }

    if extensions.is_empty() {
        println!(
            "No Flow extensions discovered. Create one with `f ext init <name>` under {}",
            flow_config::flow_root_dir().join("extensions").display()
        );
        return Ok(());
    }

View on GitHub (pinned to a747e741ae)

Solutions

  1. Use one form only: `f ext import <path>` OR `f ext <path>` — not both
  2. Drop the subcommand if you intended the shortcut: `f ext ./my-extension`
  3. Drop the bare path if you intended the subcommand: `f ext import ./my-extension`
  4. Check any wrapper script/alias that might append an extra positional argument

Example fix

// before
f ext import ./my-ext ./my-ext   # subcommand AND bare path
// after
f ext import ./my-ext            # or: f ext ./my-ext
Defensive patterns

Strategy: validation

Validate before calling

// validate CLI args before dispatching to `f ext`
let args: Vec<String> = std::env::args().skip(1).collect();
let subcommands = ["list", "doctor", "enable", "disable", "init", "import"];
let has_sub = args.first().map_or(false, |a| subcommands.contains(&a.as_str()));
let has_bare_path = args.len() > 1 && !subcommands.contains(&args[1].as_str());
if has_sub && has_bare_path {
    bail!("pass either a subcommand or a bare import path, not both");
}

Prevention

When it happens

Trigger: `f ext <subcommand> <path>` — the parser matched both an ExtAction (e.g. Import { path }) and a bare path positional, hitting the (Some(_), Some(_)) arm at src/ext.rs:22.

Common situations: Habit from other CLIs where subcommand plus positional is normal; copying an example that used the bare-path form but prepending a subcommand anyway; shell completion or wrapper script injecting an extra argument.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/da88a30e14ad8866. Report an issue: GitHub.