Hmbown/CodeWhale · error · anyhow::Error

Cloud agents are not available for this account yet; cloud…

Error message

Cloud agents are not available for this account yet; cloud dispatch fails closed (no sandbox, no push, no PR).

What it means

api_key() found no stored cloud API key, so the launcher returns the fail-closed message: cloud dispatch is refused entirely because cloud sandboxes act under your account and would otherwise have no sandbox, push, or PR capability. This is an intentional guard, not a transient fault.

Solutions

  1. Set CODEWHALE_API_KEY to a `cwc_key_…` machine key (Account → API keys in the web app)
  2. Complete the cloud onboarding/linking flow in the app
  3. Verify the key is readable by the process (env, config file, keychain)
  4. Re-confirm cloud dispatch after credentials are in place

Example fix

// before
# no key
// after
export CODEWHALE_API_KEY=cwc_key_xxxxxxxxxxxx
Defensive patterns

Strategy: validation

Validate before calling

if std::env::var("CODEWHALE_API_KEY").map_or(true, |k| !k.starts_with("cwc_key_")) {
    eprintln!("Set CODEWHALE_API_KEY (Account → API keys) before cloud dispatch");
}

Type guard

fn has_cloud_api_key() -> bool {
    std::env::var("CODEWHALE_API_KEY").map_or(false, |k| k.starts_with("cwc_key_"))
}

Try / catch

match launcher.create_sandbox(&job) {
    Err(e) if e.to_string().contains("not available for this account") => {
        prompt_user_to_link_account();
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any DaytonaLauncher operation (e.g. create_sandbox) calling Self::api_key() when read_api_key() returns None (crates/tui/src/cloud_dispatch.rs:1409).

Common situations: Fresh installs without cloud setup, users who never linked a Codewhale account, or credentials cleared from config/keychain.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/cloud_dispatch.rs:1409

                crate::tls::reqwest_blocking_client_builder()
                    .connect_timeout(std::time::Duration::from_secs(8))
                    .redirect(reqwest::redirect::Policy::none())
                    .build()
                    .map_err(|error| error.to_string())
            })
            .clone()
            .map_err(|message| anyhow!("failed to initialize the cloud agent client: {message}"))
    }

    /// The total-timeout budget for a harness-carrying client, in seconds.
    /// Public to the crate so the runner's tests can pin it against the
    /// declared `HARNESS_TIMEOUT_SECS`.
    pub(crate) fn harness_client_budget_secs(command: &HarnessCommand) -> u64 {
        u64::from(command.timeout_secs).saturating_add(Self::HARNESS_CLIENT_SLACK_SECS)
    }

    fn api_key() -> Result<String> {
        read_api_key().ok_or_else(|| anyhow!(missing_credentials_message()))
    }

    /// Control-plane URL under the validated base.
    fn control_plane_url(path: &str) -> Result<reqwest::Url> {
        let base = validate_outbound_origin(&daytona_api_url())?;
        join_api_path(base, path).context("failed to build the cloud agent request URL")
    }

    /// Toolbox base for one sandbox: `{toolboxProxyUrl}/{sandboxId}`.
    fn toolbox_base(receipt: &SandboxReceipt) -> Result<reqwest::Url> {
        if !valid_sandbox_id(&receipt.sandbox_id) {
            bail!("the sandbox id is not a usable path token");
        }
        let fallback = format!("{}/toolbox", DEFAULT_DAYTONA_API);
        let raw = receipt.toolbox_url.as_deref().unwrap_or(&fallback);
        let base = validate_outbound_origin(raw)?;
        join_api_path(base, &receipt.sandbox_id).context("failed to build the sandbox toolbox URL")
    }

View on GitHub (pinned to 73e0f67d83)