{"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":"exception","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/900007c48045867e7f44b0f9bc55ad9dbe99ee0d/crates/atuin-ai/src/models.rs#L26-L62","documentation":"`fetch_models` in atuin-ai requests the model list from the Atuin AI backend and fails when the HTTP response status is not a success. The status code is interpolated into the message, e.g. `model list request failed (500 Internal Server Error)`.","triggerScenarios":"Calling `fetch_models` when the backend returns 4xx/5xx — invalid or expired bearer token, backend outage, or network intermediary error.","commonSituations":"Expired sync/AI session token causing 401, Atuin AI service downtime, or a proxy returning 502.","solutions":["Read the status code in the message: 401/403 → re-authenticate (`atuin login` or refresh session)","Retry after confirming https://status.atuin.dev or backend availability","Check network/proxy configuration","Verify your Atuin version is current — API endpoints change"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check: only call with a live session and reachable backend\nif atuin_client::hub::HubAuthSession::is_expired(&session) {\n    session = refresh_session().await?;\n}","typeGuard":null,"tryCatchPattern":"match fetch_models(&client, &token).await {\n    Ok(models) => models,\n    Err(e) if e.to_string().contains(\"401\") => reauth_then_retry().await?,\n    Err(e) if e.to_string().contains(\"5\") => backoff_retry(3).await?,\n    Err(e) => return Err(e),\n}","preventionTips":["Refresh auth tokens before long sessions","Monitor backend status before dependent operations","Set retries with backoff for 5xx"],"tags":["http","api","network","ai"],"backgroundTag":"http-error-response","analyzedSha":"900007c48045867e7f44b0f9bc55ad9dbe99ee0d","analyzedAt":"2026-09-19T12:52:51.491Z","contentChangedAt":"2026-09-19T12:52:51.491Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}