Knowledge from the fieldRedM

_GET_TASK_FISHING (0xF3735ACD11ACD500) crashes the client even on read-only DataView access — avoid it

learning:125

_GET_TASK_FISHING (0xF3735ACD11ACD500) crashes the client even on read-only DataView access — avoid it

Context

_GET_TASK_FISHING (0xF3735ACD11ACD500) takes a ped plus an Any* struct out-param. Community fishing scripts (RedEM-RP/redemrp_fishing and derivatives) read fishingState from offset 0 to confirm that _SET_TASK_FISHING (0xF3735ACD11ACD501) actually started, using a DataView.ArrayBuffer(0xC0).

This crashes the RedM client, reproduced live on build 1491 (FXServer v1.0.0.25770, LuaGLM 5.4):

citizen-scripting-lua.dll+43E8F4
citizen-scripting-lua.dll+287B48
citizen-scripting-lua.dll+2992A9
citizen-scripting-lua.dll+2B040B
citizen-scripting-lua.dll+2DA58F
citizen-scripting-lua.dll+2B2E80
citizen-scripting-lua.dll+2B3237

60 consecutive calls with a fresh buffer per call and read-only access (GetInt32, never SetInt32) crashed the client immediately. The crash can also surface minutes later at an unrelated moment, which makes it easy to misattribute — in the original report the last fishing log line was ~135 s before the crash.

What this is NOT (all measured live, not assumed)

These are the plausible-sounding explanations that turn out to be wrong — worth listing because they send you down the wrong path:

  1. "DataView hands the native an immutable Lua string." False on this runtime. type(string.blob) == "function" under LuaGLM, so DataView.ArrayBuffer returns a real mutable blob — jo_libs' _strblob fallback (string.rep("\0", n), dataview/client.lua:5) is never reached. On a runtime without string.blob this would be a genuine additional hazard, but it is not the cause here.
  2. "Reusing one long-lived buffer is the problem." False. 40 reads through a single reused buffer plus a forced collectgarbage("collect") kept blob identity and length stable (192 bytes) with no crash.
  3. "It's allocation pressure from ArrayBuffer in a hot loop." False. The crash happens on reads alone, regardless of whether buffers are fresh or reused.

What is actually true

  • Writing into the buffer yourself is separately broken, for a concrete reason: SetFixed does self.blob = string.pack(...) (jo_libs/modules/dataview/client.lua:169-199), i.e. it replaces the blob object. Any pointer the native already holds now refers to a different string. So never SetInt32 a buffer a native is writing into — but note this was not the crash trigger, since the repro never wrote.
  • 0xC0 is an undocumented guess at the struct size. The native doc gives no size, only a pastebin link (https://pastebin.com/NmK5ZLVs). A live read showed a populated field at aligned offset 0x38 (value -1, consistent with "nothing hooked"), so fields extend well in, but the end is unknown. The most likely mechanism is the native writing past the end of an undersized buffer and corrupting the Lua heap. Not confirmed — enlarging the buffer to 0x400 was not validated as a fix.
  • Reading at 8-byte-aligned offsets is correct (standard for RDR3 struct out-params): fishingState is Int32 at offset 0. A dense 4-byte stride read yields garbage.

Recommendation

Don't call _GET_TASK_FISHING at all unless someone has verified the real struct size. To confirm the fishing task started, use observable ped state instead — the task puts a fishing rod in the ped's hands, so GetCurrentPedWeapon(ped, ...) compared against the rod hash is a crash-free substitute. _SET_TASK_FISHING itself (scalar Any p1, no buffer) is fine.

Also note these two hashes differ only in the final digit (...500 read / ...501 set), which makes transcription errors easy to miss.

Linked natives

  • _GET_TASK_FISHING (0xF3735ACD11ACD500)
  • _SET_TASK_FISHING (0xF3735ACD11ACD501)

Linked natives

  • _GET_TASK_FISHING (0xF3735ACD11ACD500)
  • _SET_TASK_FISHING (0xF3735ACD11ACD501)

Tags: fishing, dataview, crash, struct, out-param, task, luaglm
Category: natives
Created: 2026-09-09T22:07:23.964Z

Back to documentation