Hmbown/CodeWhale · error

Fleet task ` ` names custom provider=` `, but a provider…

Error message

Fleet task `{}` names custom provider=`{provider_id}`, but a provider name alone does not prove its endpoint or model; attach the live route config before creating the run

What it means

Fleet route validation rejects a task that names a provider whose id parses as ApiProvider::Custom (or fails to parse) while no live route config is attached. A provider name alone does not prove a working endpoint or model list, so run creation requires the resolved route config.

Solutions

  1. Attach the live resolved route config to the run.
  2. Switch the profile to a built-in provider id that needs no config proof.
  3. Correct the provider id typo so it parses to a known provider.

Example fix

// before
provider: "custom"  // no route config attached
// after
run.create(..., route_config=live_config)  // config proves the custom endpoint
Defensive patterns

Strategy: validation

Validate before calling

fn custom_provider_needs_config(config: Option<&RouteConfig>, provider: Option<&str>) -> bool { config.is_none() && provider.map_or(false, |p| ApiProvider::parse(p).is_none_or(|ap| ap == ApiProvider::Custom)) }

Prevention

When it happens

Trigger: validate_fleet_task_routes with config=None and the agent profile's explicit provider id being "custom" (or an unparseable provider id) for the task's effective model.

Common situations: Pointing an agent profile at a self-hosted/custom endpoint by name only, without the route config carrying the base URL and credentials; typos in provider ids that fail ApiProvider::parse; environments where route config was intentionally not shipped (configless fleet).

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/cceaad0b3ab47182. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/worker_runtime.rs:293

    for task in tasks {
        let agent_profile = resolve_task_agent_profile(task, agent_profiles)?;
        let agent_profile = agent_profile.as_deref();
        let (model, source) =
            effective_fleet_model_with_source(run_model, task.worker.as_ref(), agent_profile);
        let pinned_model = matches!(source, "task.model" | "agent_profile.model");
        let explicit_provider = explicit_fleet_provider_id(agent_profile);
        if config.is_none() && explicit_provider.is_none() {
            bail!(
                "Fleet task `{}` has no provider authority for model `{model}` (source={source}); attach the resolved route config or set the agent profile provider explicitly",
                task.id,
            );
        }
        if config.is_none()
            && let Some(provider_id) = explicit_provider.as_deref()
            && ApiProvider::parse(provider_id)
                .is_none_or(|provider| provider == ApiProvider::Custom)
        {
            bail!(
                "Fleet task `{}` names custom provider=`{provider_id}`, but a provider name alone does not prove its endpoint or model; attach the live route config before creating the run",
                task.id,
            );
        }
        if pinned_model && explicit_provider.is_none() {
            let config = config.expect("provider authority checked above");
            let (provider, base_url) = (config.api_provider(), config.active_route_base_url());
            if let Err(reason) =
                crate::route_runtime::validate_unpinned_model_provider(provider, &model, &base_url)
            {
                bail!("Fleet task `{}`: {reason} (source={source})", task.id);
            }
        }

        let route = resolve_fleet_route_with_config(task, agent_profiles, session_model, config);
        let provider = explicit_provider
            .map(|provider| format!("provider=`{provider}`"))
            .unwrap_or_else(|| {

View on GitHub (pinned to 73e0f67d83)