For performance reasons the execution time of CanvasItem rendering is not done at the same time as other things in script. This isn't documented very well, but it is also the reason why you can only use the draw functions in the
_draw() function or when signaled/notified.
If I'm remembering correctly, what happens in your code is that the
draw_texture is going to take place slightly after this function. So the GDScript function finishes first and the scoped variables are cleared off the stack and the contents are no longer available.
It's not very intuitive, and not ideal, but you can work around it in the way you mentioned, but more specifically by lifting the variable you're using to a higher scope. Then it will be alive and well for the visual server's reference later.
IE - As seen like the first line I added to your code below:
var bgTex : ImageTexture # <--- Lift var to this scope.
var bg = Image.new();
bg.create(100, 100, false, Image.FORMAT_RGBA8);
for j in range(100):
for i in range(100):
bg.set_pixel(i, j, Color(
float(i) / 100.0,
float(j) / 100.0,
bgTex = ImageTexture.new();
draw_texture(bgTex, Vector2(0.0, 0.0))