'__index' chain too long; possible loop - Help

Hi everyone! I’m trying to create a simple class, but for some reason whenever I call setmetatable, I get the error '__index' chain too long; possible loop. I’ve tried looking everywhere, but cannot seem to find the cause of this.

My code:

local proxy = {Network=network} -- Network is an external table.
setmetatable(proxy,{__index=proxy}) -- Error occurs here.

All help is greatly appreciated :smile:

1 Like

Are you sure? Setting the metatable alone shouldn’t do anything. I tried running this code and nothing happens

local proxy = {Network={}}
setmetatable(proxy, {__index=proxy})

The issue only appears when you try to READ an uninitialized value inside proxy.
Let’s break this down.

  • First you index proxy
  • Whatever you’re indexing doesn’t exist
    It will refer to the __index metamethod instead
  • The __index meta is set back to proxy
  • It tries to index proxy
  • Whatever it’s trying to index doesn’t exist
    It will refer to the __index metamethod instead

Do you see the pattern? It’s basically an endless loop. Remember that this only happens when you try to read something that doesn’t exist. You have to first write the value before accessing it.

So this following code will work:

local proxy = {Network={}}
setmetatable(proxy, {__index=proxy})

proxy.Network[1] = 1
print(proxy.Network[1]) --> 1
proxy[1] = 2
print(proxy[1]) --> 2

But if you forget to initialize the value and try reading it immediately, that’s where the error comes from:

local proxy = {Network={}}
setmetatable(proxy, {__index=proxy})

print(proxy[1])
--[[
lua: jdoodle.lua:4: '__index' chain too long; possible loop
stack traceback:
	jdoodle.lua:4: in main chunk
	[C]: in ?
]]
4 Likes

Ah alright, thanks for the help! Also, might I ask how you got the stack traceback to display the chunk information?

1 Like

im using a random online Lua interpreter to test those code lol

Makes sense. Thanks for the help though!

1 Like

@Prototrode explained the infinite chain very well. It is entered once you trigger the __index, which wakes when an empty table element is indexed. The chain that occurs when we have __index as a table is quite similar to C stack overflow when it’s recursively called as a function.

Comparable is __newindex’s behavior:

local t = {}
setmetatable(t, {__newindex = function(self, k, v)
	self[k] = v
end,})

t.key = "value" --> stack overflow

The infinite loop can be avoided by using rawset(), rawget() and rawequal(), which don’t invoke the metamethods. In normal indexing of an empty element, the C code returns 0, which is later translated to nil. Prior to returning, the interpreter automatically looks for __index.

setmetatable() requires two tables, the table and its metatable. The latter must include at least a single metamethod in order to have an effect.

For easier understanding, it helps to write the tables separately.

local t = {}
local mt = {} -- a normal table at this point
setmetatable(t, mt) -- set as a metatable, no functionality

mt.__index = t -- routing access to the table t

print(t.key) --> infinite chain again

As we can see, __index is a key in the metatable. And any attempt to directly read the property results in a recursion Prototrode explained.

print(getmetatable(t)) --> mt (containing __index)

To avoid the inconvenient error altogether, since there’s always a risk of attempting to read a non-existent property, we can use a step standardly taken in OOP.

local t = {}
t.__index = t

print(getmetatable(t)) --> nil
print(t.key) --> nil

Now the table t is routing field access to itself and contains __index as an element. From my limited knowledge of internal handling of metatables, it seems the metatable refers to a second table as a prerequisite for separating methods from metamethods, and hence getmetatable(t) doesn’t return anything.

In OOP, a single table is a common pattern and pointing to itself a good foundation to be a metatable to the object (self). However, setmetatable(t, {__index = t}) works as well, because the class functionality is accessed from self and not the class table.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.