`if false then local x end` inside a generic `for` loses the chunk's return value, panics with index out of range, or never finishes

Author: DRMacIverCreated Sep 22, 2026Updated Sep 22, 2026
  • GopherLua is a Lua5.1 implementation. You should be familiar with Lua programming language. Have you read Lua 5.1 reference manual carefully?
  • GopherLua is a Lua5.1 implementation. In Lua, to keep it simple, it is more important to remove functionalities rather than to add functionalities unlike other languages . If you are going to introduce some new cool functionalities into the GopherLua code base and the functionalities can be implemented by existing APIs, It should be implemented as a library.
  1. What version of GopherLua are you using? : v1.1.2, and master (75f4976)
  2. What version of Go are you using? : go1.27.1 darwin/arm64
  3. What operating system and processor architecture are you using? : macOS 26.6, arm64
  4. What did you do? :

A generic for whose body contains an if with a constant-false condition and a local declared in its block breaks the code that runs after that if: depending on what else is in the chunk, return 1 returns nothing, the VM panics with a Go index out of range, or the chunk never finishes. The same body with a condition that is false at run time, or under a numeric for, runs correctly:

go
package main

import (
	"context"
	"fmt"
	"strings"
	"time"

	lua "github.com/yuin/gopher-lua"
)

func run(src string) {
	L := lua.NewState()
	defer L.Close()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	L.SetContext(ctx)
	fmt.Println(src)
	if err := L.DoString(src); err != nil {
		fmt.Println("  error:", strings.SplitN(err.Error(), "\n", 2)[0])
		return
	}
	fmt.Printf("  returned %d value(s): %v\n", L.GetTop(), L.Get(1))
}

func main() {
	run(`for i = 1, 1 do end for k, v in ipairs({1}) do if false then local y end end return 1`)
	run(`local n = 0 for k, v in ipairs({1, 2, 3}) do if false then local y end n = n + v end return n`)
	run(`for k, v in ipairs({1}) do if false then local y end end return 1`)
	run(`local n = 0 for k, v in ipairs({1, 2, 3}) do if v == 9 then local y end n = n + v end return n`)
	run(`local n = 0 for i = 1, 3 do if false then local y end n = n + i end return n`)
}
  1. What did you expect to see? : Each chunk returns the value it ends with: 1, 6, 1, 6, 6.

  2. What did you see instead? :

for i = 1, 1 do end for k, v in ipairs({1}) do if false then local y end end return 1
  error: <string>:1: context deadline exceeded
local n = 0 for k, v in ipairs({1, 2, 3}) do if false then local y end n = n + v end return n
  error: runtime error: index out of range [16] with length 16
for k, v in ipairs({1}) do if false then local y end end return 1
  returned 0 value(s): nil
local n = 0 for k, v in ipairs({1, 2, 3}) do if v == 9 then local y end n = n + v end return n
  returned 1 value(s): 6
local n = 0 for i = 1, 3 do if false then local y end n = n + i end return n
  returned 1 value(s): 6

The first chunk runs until the three-second context stops it; without SetContext it was still running after 25 seconds. The second is a Go panic recovered by DoString. The third returns no value for return 1; it differs from the first only by the empty for i = 1, 1 do end in front, so which of the three symptoms appears depends on what precedes the loop. The dead branch needs a local in its block (if false then y = 1 end and if false then end are fine), and the enclosing loop must be a generic for (a numeric for or a while with the same body is fine). if false then break end, and a nested for k6 in pairs({}) do end in the dead block, behave the same way; with two locals declared before the loop and return a, b after it, the chunk returned a function and a table in place of the numbers.

Tested on gopher-lua v1.1.2 and on current master (75f4976).

BTW, this was found by an automated program that writes property-based tests for various open source projects using hegel (but it has been reviewed by hand before reporting). We've also potentially found (but not yet hand validated) 71 other bugs in gopher-lua. You can see the tests at https://github.com/hegeldev/hegel-zoo/tree/main/targets/go/gopher-lua. Let us know if you would like us to file the other bugs found and/or contribute the tests. NB the tests are currently LLM generated and probably not yet suitable for inclusion as is, but we're happy to help get them into a better state if you want them.