Skip to Content
ScriptingCancellation & Latent Events

Event cancellation and latent delivery

A local event can let another resource veto an action. A latent network event can transfer a larger payload without monopolizing the normal event channel. Neither feature replaces server validation, and neither is an automatic application-level acknowledgement.

Start with event direction and security. This guide covers what happens after you have selected the correct runtime and recipient.

Cancellation is not a return value from your handler

CancelEvent() marks the current event as canceled. It does not prevent other handlers for that event from running. WasEventCanceled() inspects the last triggered local event; it is not a way to ask a remote machine whether it accepted your request. Lua’s TriggerEvent additionally returns whether that local event was canceled.

A handler returning false is not the same operation as CancelEvent(). A native engine event can only prevent its underlying action where that event’s documented contract permits cancellation. Check the client or server contract rather than assuming every notification is cancelable. These distinctions follow the official cancellation guide .

Build a local veto before the side effect

Create resources/[local]/doc_event_gate/fxmanifest.lua:

fx_version 'cerulean' game 'gta5' client_script 'client.lua'

Create client.lua:

local gate = AddEventHandler('doc_event_gate:beforeNote', function(text) if type(text) ~= 'string' or #text == 0 or #text > 80 then CancelEvent() end end) RegisterCommand('docnote', function(_, args) local text = table.concat(args, ' ') local canceled = TriggerEvent('doc_event_gate:beforeNote', text) if canceled then print('Note rejected: use 1 to 80 bytes of text.') return end print(('Local note: %s'):format(text)) end, false) AddEventHandler('onClientResourceStop', function(name) if name == GetCurrentResourceName() then RemoveEventHandler(gate) end end)

Run refresh and ensure doc_event_gate in the server console. In F8, docnote hello should print a note, while docnote without text should be rejected. Lua’s string length here counts bytes, not user-perceived characters; this deliberate small diagnostic is not a multilingual text-length validator.

The command performs the side effect only after checking the veto. A second handler that prints immediately would still print even if the gate cancels the event. Do not depend on handler order to implement a transaction. Keep this immediate validation non-yielding; a later asynchronous result needs an explicit request/reply protocol instead of trying to undo an already completed action.

This client-local example has no authority over money, items or permissions. For those operations the server must own the validation and the mutation.

Choose normal or latent transport

DirectionNormal Lua callLatent Lua call
Client to serverTriggerServerEvent(name, ...)TriggerLatentServerEvent(name, bps, ...)
Server to clientTriggerClientEvent(name, target, ...)TriggerLatentClientEvent(name, target, bps, ...)

Use ordinary events for small requests. Consider latent delivery for genuinely larger payloads after removing fields the recipient does not need. bps means bytes per second, not bits or a timeout. The documented default for zero or minus one is 25000. On server broadcasts, the budget is per recipient: targeting -1 increases aggregate traffic with the number of players.

The official transport guide  explains the channel and targeting behavior. Raising bps to an extreme value defeats the reason for throttling. Estimate transfer duration from serialized size and the chosen budget, but do not treat that estimate as a delivery deadline or a bandwidth guarantee.

Send a bounded test payload to one client

For a separate network test, create doc_latent with this manifest:

fx_version 'cerulean' game 'gta5' server_script 'server.lua' client_script 'client.lua'

Create server.lua. Only the server console can invoke this example:

RegisterCommand('docnotice', function(commandSource, args) if commandSource ~= 0 then return end local target = tonumber(args[1]) if not target or target < 1 or target % 1 ~= 0 then print('Usage: docnotice <connected server ID>') return end if not GetPlayerName(target) then print('That player is not connected.') return end local text = string.rep('A bounded documentation transfer.\n', 400) if #text > 65536 then return end TriggerLatentClientEvent('doc_latent:notice', target, 25000, text) end, true)

Create client.lua:

RegisterNetEvent('doc_latent:notice', function(text) if source ~= 65535 then return end if type(text) ~= 'string' or #text > 65536 then return end print(('Received documentation payload: %d bytes'):format(#text)) end)

Start doc_latent, then run docnotice with the connected test client’s server ID in the server console. Only that client should print a received-byte count. The code prints the size rather than dumping a large payload. Repeat with two connected clients and verify that the other client receives nothing.

The source check on the client identifies the expected server-originated event context; it does not make client state trustworthy. This example intentionally exposes no client-to-server upload endpoint. A real upload needs bounded schema validation, per-player rate limits, server-owned authorization and disconnect cleanup. A slower transport is not protection against an excessive or malicious payload.

Verify failure behavior, not just a successful send

Disconnect the target during the test and observe that the command has no end-to-end acceptance response. For a real operation that needs confirmation, design a reply with a request ID, timeout and duplicate handling. Never interpret a remote CancelEvent or a successful trigger call as proof that a database write completed.

Stop doc_event_gate and doc_latent and remove their startup entries afterward. The code is source-reviewed documentation; cancellation dispatch, network behavior and disconnect handling still require actual FiveM client/server acceptance. No simulated network test or website build proves those engine behaviors.