{"record":{"id":"9d724363acecaf4c","repo":"gitbutlerapp/gitbutler","slug":"remote-err","errorCode":null,"errorMessage":"{remote}: {err}","messagePattern":"\\{remote\\}: \\{err\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/workspace.rs","lineNumber":323,"sourceCode":"            return Ok(Some(ignored));\n        }\n\n        // Keep the failure's own error context (e.g. the `ProjectGitAuth` code and its\n        // user-facing message) intact whenever possible: return a single failure as-is, and\n        // reapply the first failure's code when several failures collapse into one message.\n        Err(if failures.len() == 1 {\n            let (remote, err) = failures.into_iter().next().expect(\"length checked above\");\n            err.context(format!(\"fetching remote `{remote}` failed\"))\n        } else {\n            let code = failures\n                .iter()\n                .find_map(|(_, err)| err.custom_context().map(|ctx| ctx.code));\n            let joined = failures\n                .iter()\n                .map(|(remote, err)| format!(\"{remote}: {err}\"))\n                .collect::<Vec<_>>()\n                .join(\"\\n\");\n            let err = anyhow::anyhow!(joined);\n            match code {\n                Some(code) => err.context(code),\n                None => err,\n            }\n        })\n    })();\n\n    let attempted_ms = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .map_err(|err| anyhow::anyhow!(\"system clock is before the Unix epoch: {err}\"))?\n        .as_millis()\n        .try_into()\n        .map_err(|err| anyhow::anyhow!(\"fetch timestamp does not fit in the database: {err}\"))?;\n    let _guard = ctx.exclusive_worktree_access();\n    match &fetch_result {\n        Ok(ignored_errors) => ctx\n            .db\n            .get_cache_mut()?","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/workspace.rs#L305-L341","documentation":"`workspace_fetch_from_remotes` aggregates failures from fetching multiple remotes. When any remote fetch fails, it joins all per-remote errors as '{remote}: {err}' lines into a single anyhow error, optionally attaching the first available error code as context.","triggerScenarios":"Calling `workspace_fetch` when one or more configured remotes fail to fetch: network unreachable, bad credentials, missing remote, rejected refs, or remote-side errors.","commonSituations":"Offline work; expired forge credentials; a remote was deleted server-side; DNS/proxy issues; a colleague force-pushed causing fetch rejections.","solutions":["Read the per-remote messages to see which remotes failed and why","Run `git fetch <remote>` manually for the failing remote to reproduce","Fix credentials (`but config forge auth`) or network/proxy settings","Remove or fix stale remotes via `git remote` if a remote no longer exists"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check remotes are reachable/configured\nfor remote in repo.remotes()? {\n    let url = repo.find_remote(&remote)?.url().map(String::from);\n    if url.is_none() { eprintln!(\"remote {remote} has no URL\"); }\n}","typeGuard":null,"tryCatchPattern":"match workspace_fetch(ctx).await {\n    Err(e) => {\n        // message contains per-remote lines: \"remote: err\"\n        for line in format!(\"{e:#}\").lines() {\n            eprintln!(\"fetch failure: {line}\");\n        }\n        Err(e)\n    }\n    ok => ok,\n}","preventionTips":["Keep remotes and credentials valid; re-auth with `but config forge auth`","Prune deleted remotes (`git remote prune`, or remove them)","Check network/proxy before batch fetches"],"tags":["git","network","fetch","remotes"],"backgroundTag":"git-command-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}