← back to Exo
Fix first start in offline mode crash (#1782)
248919c2a821d6e923aca6b7b4f789b9210bcd78 · 2026-03-24 13:58:23 +0000 · ciaranbor
## Motivation
Running exo in offline mode on a machine where a model has never been
downloaded causes a crash
## Changes
- `download_shard` now catches `FileNotFoundError` from
`fetch_file_list_with_cache` and returns a `not_started` progress
instead of propagating the exception
## Why It Works
A status query should never crash its caller. By returning
`not_started`, the coordinator's existing offline guard (`if
self.offline:` at line 198) is reached and emits a graceful
`DownloadFailed` event. This also eliminates the warning spam on startup
where the status iterator catches the same exception for every
predefined model.
## Test Plan
### Manual Testing
- Start exo with `--offline` on a machine with no cached models, request
a model via the API — should get a graceful failure instead of a crash
Files touched
M src/exo/download/download_utils.py
Diff
commit 248919c2a821d6e923aca6b7b4f789b9210bcd78
Author: ciaranbor <81697641+ciaranbor@users.noreply.github.com>
Date: Tue Mar 24 13:58:23 2026 +0000
Fix first start in offline mode crash (#1782)
## Motivation
Running exo in offline mode on a machine where a model has never been
downloaded causes a crash
## Changes
- `download_shard` now catches `FileNotFoundError` from
`fetch_file_list_with_cache` and returns a `not_started` progress
instead of propagating the exception
## Why It Works
A status query should never crash its caller. By returning
`not_started`, the coordinator's existing offline guard (`if
self.offline:` at line 198) is reached and emits a graceful
`DownloadFailed` event. This also eliminates the warning spam on startup
where the status iterator catches the same exception for every
predefined model.
## Test Plan
### Manual Testing
- Start exo with `--offline` on a machine with no cached models, request
a model via the API — should get a graceful failure instead of a crash
---
src/exo/download/download_utils.py | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/src/exo/download/download_utils.py b/src/exo/download/download_utils.py
index 3f6f1dc9..ba9e7742 100644
--- a/src/exo/download/download_utils.py
+++ b/src/exo/download/download_utils.py
@@ -744,13 +744,30 @@ async def download_shard(
logger.debug(f"Downloading {shard.model_card.model_id=} with {allow_patterns=}")
all_start_time = time.time()
- file_list = await fetch_file_list_with_cache(
- shard.model_card.model_id,
- revision,
- recursive=True,
- skip_internet=skip_internet,
- on_connection_lost=on_connection_lost,
- )
+ try:
+ file_list = await fetch_file_list_with_cache(
+ shard.model_card.model_id,
+ revision,
+ recursive=True,
+ skip_internet=skip_internet,
+ on_connection_lost=on_connection_lost,
+ )
+ except FileNotFoundError:
+ not_started_progress = RepoDownloadProgress(
+ repo_id=str(shard.model_card.model_id),
+ repo_revision=revision,
+ shard=shard,
+ completed_files=0,
+ total_files=0,
+ downloaded=Memory.from_bytes(0),
+ downloaded_this_session=Memory.from_bytes(0),
+ total=Memory.from_bytes(0),
+ overall_speed=0.0,
+ overall_eta=timedelta(0),
+ status="not_started",
+ file_progress={},
+ )
+ return target_dir, not_started_progress
filtered_file_list = list(
filter_repo_objects(
file_list, allow_patterns=allow_patterns, key=lambda x: x.path
← 49951e1b Sync custom model cards across nodes (#1768)
·
back to Exo
·
Improve batch performance and stats reporting (#1777) 7df3774c →