assert() failure swallowed when used with load() inside pcall() — lua_pcall() returns 0 and stack is empty

155 views
Skip to first unread message

sachin varshney

unread,
Apr 9, 2025, 5:46:29 AM4/9/25
to lua-l
Hi Lua Team,

Recently we upgraded lua library from lua-5.0.2 to lua-5.4.6, we've observed what may be an unexpected behavior when
using assert() within Lua code that's executed via lua_pcall() from the C host application.

Summary ;
==========
We run this Lua snippet through load() and pcall():

local func, err = load("assert(false, 'my error')")
local success, result = pcall(func)

When executed from C using lua_pcall(), we observe the following:

lua_pcall() returns 0, which indicates success,

The Lua stack is empty (lua_gettop(L) == 0),

There is no error message or return values on the stack.

This makes it impossible to tell from the C side that an assert() failure occurred — which is highly misleading, as no error is propagated.

Test Setup (C Code) :

lua_getglobal(L, "dobuffer");
lua_pushstring(L, "assert(false, 'fail message')");

int ret = lua_pcall(L, 1, 2, 0);

int top = lua_gettop(L);
printf("lua_pcall returned: %d, stack top = %d\n", ret, top);

if (ret != 0) {
    printf("Lua Error: %s\n", lua_tostring(L, -1));
} else if (top == 0) {
    printf("Unexpected: lua_pcall returned success, but stack is empty.\n");
}

Additional Logs from Lua Internals :
=====================================

luaB_assert invoked  
luaD_rawrunprotected invoked 1: 2  
luaD_closeprotected for loop ret = 2  
luaD_rawrunprotected invoked 1: 0  
Result output displayed = 0
From this, it appears an error (ret = 2) was caught and suppressed internally, leaving the C API (lua_pcall) unaware that an error happened.

❗ Why This Matters
This breaks the expected contract between Lua and host C code. From the C side, we rely on lua_pcall() to return non-zero on error — if the stack is empty and ret == 0, we assume everything went fine.

❓ Request for Clarification
Is this behavior expected? Should assert(false, "msg") inside load() always be considered recoverable?

If so, what is the recommended way to raise a Lua error from within such a loaded string so that C can detect it?

If this behavior isn't intended, could it possibly be related to the luaB_assert or luaD_rawrunprotected logic?
We're happy to share more details or context from our side if it helps clarify the issue


Thanks and best regards,
Varshney Sachin 

Sainan

unread,
Apr 9, 2025, 6:58:12 AM4/9/25
to lu...@googlegroups.com
To me, it looks like the issue would be in your `dobuffer` function, as this is not included in my copy of Lua 5.4.

-- Sainan

Scott Morgan

unread,
Apr 9, 2025, 7:11:47 AM4/9/25
to lu...@googlegroups.com
On 09/04/2025 06:05, sachin varshney wrote:
> Hi Lua Team,
>
> Recently we upgraded lua library from lua-5.0.2 to lua-5.4.6, we've
> observed what may be an unexpected behavior when
> using assert() within Lua code that's executed via lua_pcall() from the
> C host application.
>
> Summary ;
> ==========
> We run this Lua snippet through load() and pcall():
>
> local func, err = load("assert(false, 'my error')")
> local success, result = pcall(func)
>

Can't reproduce:

Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio
> func, err = load("assert(false, 'my error')")
> = pcall(func)
false [string "assert(false, 'my error')"]:1: my error
>


Gé Weijers

unread,
Apr 10, 2025, 2:00:52 AM4/10/25
to lu...@googlegroups.com
On Wed, Apr 9, 2025 at 2:46 AM sachin varshney <solidsa...@gmail.com> wrote:
Hi Lua Team,

Recently we upgraded lua library from lua-5.0.2 to lua-5.4.6, we've observed what may be an unexpected behavior when
using assert() within Lua code that's executed via lua_pcall() from the C host application.
[...]

I tried to reproduce your problem. 
I added this code to a module called 'util' (I have some extensions in that module, it's not important)

static int exec_fun(lua_State *L)
{
    lua_getglobal(L, "dobuffer");
    lua_pushliteral(L, "assert(false, 'oops')");
    int ret = lua_pcall(L, 1, LUA_MULTRET, 0);
    if (ret != LUA_OK)
        fprintf(stderr, "Error caught\n");
    return lua_gettop(L);
}


Then I ran this code;

local util = require "util"

function do_string(s)
    local f = load(s)
    f()
end

print(util.exec_fun())

Output:
Error caught
[string "<code>"]:1: oops

I can't reproduce your issue with the information you have given. What's in your "dobuffer"?

--

Gé Weijers

unread,
Apr 10, 2025, 2:04:23 AM4/10/25
to lu...@googlegroups.com
I made a copying error, I renamed dobuffer to do_string but forgot to copy the C code in my email..
--

sachin varshney

unread,
Apr 11, 2025, 9:22:57 AM4/11/25
to lua-l
Hi All, 

Thanks for the quick help & response, sorry I am new to lua , I check the code of dobuffer and found still we are using loadstring instead of load (sorry , i overlooked that part of code).
Its working now , once again thanks much for pointing out about dobuffer.

Thanks & Regards
Varshney Sachin

Reply all
Reply to author
Forward
0 new messages