Don't start a new traceback for spawned threads

As a Roblox developer, it’s currently too hard to debug threads that were spawned. An entirely new stack trace begins when you call task.spawn, task.delay, etc, and it makes debugging those functions a lot more difficult in most cases. Here’s an example.

local function cleanup(something: any)
	if typeof(something) == "Instance" then
		something:Destroy()
	else
		error("Something terribly wrong happened!")
	end
end

cleanup(workspace.Gravity)

--[[ Traceback:
	Line 5
	Line 9
]]

Let’s say this code errors. It’s very easy to debug in this case, all you need to do is turn on Debug Error > On Unhandled Exceptions. You can step out of the function and see what called it, what arguments were passed, and generally how the code got there. But this goes down the drain when you use task.spawn.

task.spawn(cleanup, workspace.Gravity)

--[[ Traceback:
	Line 5
]]

Now you get an error and you can’t find out what code caused it! I propose that the spawned threads inherit the stack trace of the one who spawned it, so you can tell what called it. We could fix a lot of bugs more effectively with this.

This is in Feature Requests > Studio Features because I believe this should only happen in Studio. It wouldn’t make sense to keep the stack traces for spawned threads in the live experience. That may slightly impact performance for no benefit. In Studio though, it would pay off!

1 Like

It feels like here’s something wrong with the idea of the stack trace…

Nonetheless, You can help yourself with something like this:

ModuleScript:

--!strict
local module = {}

local function spawn_thread(method : (traceback : string, any) -> (), ...)
	local traceback = debug.traceback()
	task.spawn(method, traceback, ...)
end

module.spawn_thread = spawn_thread

return module

LocalScript:

--!strict

local m = require(game.ReplicatedStorage.ModuleScript)

local function cleanup(traceback : string, something: any)
	if typeof(something) == "Instance" then
		something:Destroy()
	else
		print("\n" .. traceback)
		error("Something terribly wrong happened!")
	end
end

m.spawn_thread(cleanup, workspace.Gravity)

This solution is not going to work for 99.9% of cases. In my game, I have an error caused by some line out of the thousands of lines of code in my game, where the error happens in a large library.

The trace starts and ends in the library, no way to tell what caused the issue. There’s no way I’m going to sift through all of these task.spawn and add a mechanism that lets me debug the issue. I would need to pass that traceback value through multiple methods in the library (that I did not make) to make it work too.

So no, a workaround like this won’t work. It needs to be built-in at least for Studio to make debugging easier.

thanks for the feedback!

Create wrapper for global task then and use when you need it?
Come on, I provided a sample, I can’t think for yourself: roblox already has tools for that, no need to make roblox more complex for rare cases.

LocalScript:

--!strict
--!nolint BuiltinGlobalWrite

task = require(game.ReplicatedStorage.ModuleScript)

local function cleanup(something: any)
	if typeof(something) == "Instance" then
		something:Destroy()
	else
		error("Something terribly wrong happened!")
	end
end

task.spawn(cleanup, workspace.Gravity)

ModuleScript:

--!strict

local original_task = task

local function spawn_thread(f : (any) -> any, ...)
	local traceback = debug.traceback()
	local params = ...
	original_task.spawn(function()
		local ok, result = pcall(f, params)
		if not ok then
			assert(false, "\n" .. tostring(result) .. "\n" .. traceback)
		end
	end)
end

local module = {}

module.spawn = spawn_thread

return module

The concern you’ve raised about spawned threads starting a new traceback rather than inheriting the parent stack context is a highly relevant issue for debugging workflows in Roblox Studio. Debugging asynchronous code is already challenging, and when functions like task.spawn or task.delay discard the original stack trace, developers lose critical context that would otherwise help them pinpoint the source of an error.

In the example you provided, calling cleanup(workspace.Gravity) directly produces a traceback that includes both the line where the error occurred and the line where the function was invoked. This is the expected behavior and provides developers with the full chain of execution. However, when the same function is invoked via task.spawn, the traceback is truncated, showing only the line where the error occurred. This loss of context makes it significantly harder to identify the origin of the problem, especially in larger projects where functions are spawned from multiple locations.

Key points to consider:

  • Debugging accuracy: Full stack traces are essential for identifying not just what failed, but where and why. Losing the parent context forces developers to guess which part of the code triggered the error.
  • Consistency with synchronous execution: Direct calls preserve the full traceback, while asynchronous calls do not. Aligning these behaviors would make debugging more predictable and reduce confusion.
  • Impact on developer productivity: Without full tracebacks, developers spend more time reproducing errors or adding manual logging. This slows down iteration and increases frustration.
  • Industry standards: Many modern languages and frameworks (such as Python’s asyncio or JavaScript’s async/await) provide mechanisms to preserve stack context across asynchronous boundaries. Implementing similar behavior in Roblox Studio would align with professional development practices.
  • Performance considerations: The proposal to preserve stack traces only in Studio, not in live experiences, is a practical compromise. It avoids runtime overhead in production while giving developers the tools they need during debugging.
  • Implementation feasibility: Since Studio already captures full tracebacks for synchronous calls, extending this capability to spawned threads is technically achievable. It would involve propagating the parent stack context into the new thread during initialization.

The benefits of implementing this change are clear:

  • Improved debugging workflows: Developers can immediately see both the error location and the originating call, reducing guesswork.
  • Reduced reliance on manual logging: With full tracebacks, developers no longer need to insert additional print statements or error handlers to reconstruct context.
  • Enhanced accessibility for new developers: Beginners often struggle with debugging asynchronous code. Providing complete tracebacks lowers the learning curve and makes Studio more approachable.
  • Community trust: Addressing this issue demonstrates Roblox’s commitment to refining Studio as a professional‑grade tool. Developers notice when friction points are resolved, and it builds confidence in the platform.

In summary, ensuring that spawned threads inherit the parent stack trace in Roblox Studio would significantly improve debugging accuracy, align Studio with industry standards, and reduce frustration for developers. By limiting the change to Studio, performance concerns in live experiences are avoided, while developers gain the full context they need during development. This adjustment is both technically feasible and highly impactful, and it would be welcomed by the community as a meaningful enhancement to the debugging experience.

Thank you for this detailed explanation!

It seems to me that you have not provided enough key points to consider, I’d like to add following:

  • more complex runtime code base. Less code - less bugs, simple as that.
  • production and studio runtime differences. I am in no way aware if current runtime is the same, but anyway more differences increases complexity that affects product maintenance and future extensions.
  • the use case might be considered as “rare” to accept efforts and potential effects on product following suggested in OP solution.
  • if the problem occurs in production, suggested solution does not help.
  • alternative solution can be implemented by any user as described in the post, it can be published also and problems can be monitored in production.

Thank you!

Edit: Could you please pass this to your chatgpt or what ever you use? Thank you!

3 Likes