{"record":{"id":"fefbd75ca96cc296","repo":"atuinsh/atuin","slug":"model-list-request-failed-status","errorCode":null,"errorMessage":"model list request failed ({status})","messagePattern":"model list request failed \\((.+?)\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-ai/src/models.rs","lineNumber":44,"sourceCode":"}\n\n/// Fetch the models available to this user. Sent authenticated because the\n/// server includes feature-flag-gated models only for entitled users.\npub async fn fetch_models(endpoint: &reqwest::Url, token: &str) -> Result<ModelList> {\n    let url = endpoint.append_path(\"api/cli/models\")?;\n\n    let mut request = reqwest::Client::new()\n        .get(url)\n        .header(USER_AGENT, crate::stream::APP_USER_AGENT)\n        .timeout(Duration::from_secs(10));\n    if !token.is_empty() {\n        request = request.bearer_auth(token);\n    }\n    let response = request.send().await.context(\"failed to fetch model list\")?;\n\n    let status = response.status();\n    if !status.is_success() {\n        eyre::bail!(\"model list request failed ({status})\");\n    }\n\n    response.json::<ModelList>().await.context(\"failed to parse model list\")\n}\n\n/// Persist the chosen alias to `ai.model` in config.toml so it becomes the\n/// default for future sessions. Already-running sessions keep the model they\n/// read at startup.\npub async fn save_model_selection(alias: &str) -> Result<()> {\n    let config_file = atuin_client::settings::Settings::get_config_path()?;\n    let config_str = tokio::fs::read_to_string(&config_file).await.unwrap_or_default();\n    let mut doc = config_str.parse::<toml_edit::DocumentMut>()?;\n\n    if !doc.contains_key(\"ai\") {\n        doc[\"ai\"] = toml_edit::table();\n    }\n    doc[\"ai\"][\"model\"] = toml_edit::value(alias);\n","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/atuinsh/atuin/blob/c0c717ab04c881764bcad4b3d169a507e2432643/crates/atuin-ai/src/models.rs#L26-L62","documentation":"`fetch_models` requests the model list from the AI gateway; when the HTTP response status is not a success, it bails with the status code embedded in the message. This distinguishes a rejected/failed HTTP request from transport or JSON-parsing failures.","triggerScenarios":"The model-list endpoint returns 4xx/5xx: expired or invalid bearer token, wrong gateway address, server error, or rate limiting","commonSituations":"Stale auth session after logout, misconfigured hub/base URL, gateway outage or maintenance","solutions":["Check the status code in the message (401/403 => re-authenticate with the hub)","Re-run `atuin login` / refresh the AI session token","Verify the configured gateway URL is correct","Retry later if the status is 5xx (server-side issue)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// preflight: check token and endpoint before fetching models\nlet status = client.get(&url).bearer_auth(token).send().await?.status();\nif status == reqwest::StatusCode::UNAUTHORIZED { reauth().await?; }","typeGuard":null,"tryCatchPattern":"match fetch_models(token).await {\n    Ok(models) => models,\n    Err(e) if e.to_string().contains(\"401\") || e.to_string().contains(\"403\") => {\n        reauthenticate().await?;\n        fetch_models(token).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Refresh hub auth tokens before long-running AI sessions","Monitor gateway status; treat 5xx as transient and back off","Log the status code from the message for triage"],"tags":["http","api","network"],"backgroundTag":"http-error-response","analyzedSha":"c0c717ab04c881764bcad4b3d169a507e2432643","analyzedAt":"2026-09-12T07:40:01.341Z","contentChangedAt":"2026-09-12T07:40:01.341Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}