You have hitted the "ambiguous soft scope case".
In short: the assignment of a local variable inside a (soft) local scope depends if the code is inside a "REPL context"
or not.
For "REPL context" I mean the REPL and all environments that behaves as the REPL in this case, for example
Jupyter:
julia> i = 0
julia> while i < 3
i += 1
@info i
end
[ Info: 1
[ Info: 2
[ Info: 3
Instead code from non interactive context like file, eval and Pluto acts like that:
julia> code = """
i = 0
while i < 3
i += 1
@info i
end
"""
julia> include_string(Main, code)
┌ Warning: Assignment to `i` in soft scope is ambiguous because a global variable by the same name exists: `i` will be treated as a new local. Disambiguate by using `local i` to suppress this warning or `global i` to assign to the existing global variable.
└ @ string:3
ERROR: LoadError: UndefVarError: i not defined
All of this has been designed to ensure both the convenience of REPL usage and to avoid unwanted side effects of using julia on a large scale.
Full details here.
To fix the problem you may be use global
as already suggested or enclose your code inside a function.