Skip to content

Reading errors

Something is not working. This page is the order to check things in.

Where messages appear

print and every error go to the script console of the object's owner — which is you, while you are building.

luau
print("touched by " .. player.name)

It is for finding out what your script is doing. It is not how you talk to players; for that, see Menus and HUDs.

Up to 32 lines per event, 512 bytes per line. A print inside a fast timer will be trimmed, which is itself a useful signal.


The three kinds of not-working

1. Nothing happens at all

The script did not load, or the handler was never registered.

Save the script and watch the console. A script that fails while loading reports it there and then, and none of its handlers exist afterwards — so the symptom is silence rather than an error at the moment you click.

Check:

  • Is the script actually attached to the object you are clicking?
  • Did you save?
  • Is there an error from the moment you saved?

2. It worked once and then stopped

The script was killed. Look for one of these in the console:

MessageMeaning
script budget exceededone event used more than 50,000 instruction units
script memory limit exceededit went over 4 MB

Both mean a loop without an exit. The script does not run again until you save it.

luau
-- kills the script
while true do
    self:rotate_by(0, 1, 0)
end

-- what you meant
self:on_timer(0.05, function()
    self:rotate_by(0, 1, 0)
end)

3. It half works

An ordinary error abandoned that one event. The script is still alive and the next click will run normally. The message names the line.


The messages you will actually see

attempt to index nil

You read a field of something that does not exist.

luau
local p = self:part("hinge")
p:rotate_by(0, 5, 0)          -- if there is no "hinge", p is nil

The fix is always the same shape — check, bail out, carry on:

luau
local p = self:part("hinge")
if not p then
    print("no part called 'hinge'")
    return
end
p:rotate_by(0, 5, 0)

This is the single most common script error. Anything that can answer nilself:part, players:get, objects:get, self.store:get, self.contents:get, world:raycast — deserves the check.

attempt to call a nil value

You called something that is not a function. Usually one of:

  • a dot where a colon belongs. self.move_to(0, 5, 0) instead of self:move_to(0, 5, 0)
  • a typo in a method name. self:rotateby instead of self:rotate_by
  • a method that does not exist. Check it against the reference rather than guessing — a plausible name is not a real one

attempt to perform arithmetic / concatenate

You mixed text and numbers.

luau
print("coins: " .. 5)          -- fine
print("coins: " .. nil)        -- error
local n = "5" + 1              -- error

self.store always hands back text, so this bites there most often:

luau
local n = tonumber(self.store:get("coins") or "0") or 0

unknown module '…'

require with a name that is not a built-in and is not a sibling script.

Built-ins are self, events, players, vec, world, objects.

For your own module, attach a sibling script with that name to the same object, and then re-save the script that requires it — the module has to exist first.

cyclic require of module '…'

Two of your modules require each other. One of them has to stop.


When there is no error at all

The hardest case: nothing is red, and nothing happens.

Check the return value

Most verbs refuse in silence unless you look.

luau
local ok, why = self:hold({ player = p.id })
if not ok then
    print("hold refused: " .. why)     -- "permission", "held", "full"…
end

If a call seems to do nothing, print its second return value. The reason is almost always waiting there. Every one of them is listed in Limits and refusals.

Count your effects

Eight world effects per event. The ninth silently does not happen.

luau
for i = 1, 20 do
    world:spawn({ shape = "sphere" })   -- only 8 of these exist
end

Check you are not writing the same thing repeatedly

A timer that writes properties every tick, whether or not anything changed, spends the region's budget for nothing and can crowd out the writes that matter. Act when the answer changes:

luau
if should ~= lit then
    lit = should
    set_all(lit)
end

Check whose screen it is

A verb that draws on a player refuses from a personal script aimed at somebody else, and answers nil, reason. Print it.


Working out where you are

When you cannot tell which branch ran, print the branch, not the value:

luau
self:on_touch(function(p)
    print("touch: entered")

    if self:held_by() then
        print("touch: already held, leaving")
        return
    end

    print("touch: holding")
    local ok, why = self:hold({ player = p.id })
    print("touch: hold said " .. tostring(ok) .. " " .. tostring(why))
end)

Ugly, and it finds the problem in one click. Take them out afterwards.

Reading the code twice and finding nothing means stop reading. A defect of order — two things writing the same thing in one frame, a handler running before the thing it needs — is invisible on the page and obvious in a log. One line per event beats another read-through.

Next

Hungrit scripting documentation.