Skip to content

Fix load_log_files UnboundLocalError when all FileNotFoundError retries fail#3858

Open
Chessing234 wants to merge 1 commit intolm-sys:mainfrom
Chessing234:fix/load-log-files-unbound-lines-after-retries
Open

Fix load_log_files UnboundLocalError when all FileNotFoundError retries fail#3858
Chessing234 wants to merge 1 commit intolm-sys:mainfrom
Chessing234:fix/load-log-files-unbound-lines-after-retries

Conversation

@Chessing234
Copy link
Copy Markdown

Bug

fastchat/serve/monitor/basic_stats.py::load_log_files opens the log file inside a 5-attempt retry loop and then iterates lines:

def load_log_files(filename):
    data = []
    for retry in range(5):
        try:
            lines = open(filename).readlines()
            break
        except FileNotFoundError:
            time.sleep(2)

    for l in lines:
        ...

If the file never becomes available (wrong path, producer hasn't written it yet, stale watch), every iteration hits the except branch. The loop completes without executing lines = ..., so the following for l in lines: raises UnboundLocalError: local variable 'lines' referenced before assignment. That error buries the real cause — FileNotFoundError — and points at a line where nothing is actually wrong.

load_log_files_parallel calls this through a Pool.imap, so on a missing shard the traceback surfaced to the caller is UnboundLocalError with no filename, instead of a clear "this file is missing."

Root cause

The retry loop swallows FileNotFoundError but has no terminal branch for "all retries exhausted." Python doesn't care that the symbol is annotated in the try; the binding only happens on a successful open().

Why the fix is correct

Attach a for/else whose else only runs when the loop wasn't broken out of — i.e. when every retry failed — and re-raise the real exception with the filename:

     for retry in range(5):
         try:
             lines = open(filename).readlines()
             break
         except FileNotFoundError:
             time.sleep(2)
+    else:
+        raise FileNotFoundError(f"Failed to open {filename} after 5 retries")

     for l in lines:
  • Happy path: break skips the else, behaviour is unchanged.
  • Failure path: the UnboundLocalError is replaced by FileNotFoundError naming the actual file, matching the exception that the loop is explicitly retrying.

Two added lines; no other changes in this function or file.

…ries fail

load_log_files opens the log file inside a retry loop:

    for retry in range(5):
        try:
            lines = open(filename).readlines()
            break
        except FileNotFoundError:
            time.sleep(2)

    for l in lines:
        ...

If the file never becomes available (e.g. the caller passed a bad path,
or the producer never writes it), every iteration hits the except branch,
the loop completes without binding lines, and the next statement raises
'UnboundLocalError: local variable lines referenced before assignment' --
obscuring the real cause (FileNotFoundError) with a confusing local-scope
error that points nowhere useful.

Adding a for/else that raises FileNotFoundError after the retries run out
turns this into a correct, explicit failure naming the actual file. The
break in the happy path keeps the else branch unreached on success, so
there is no behaviour change when the file opens within 5 tries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant