Skip to content

Diagnostics

The SDK emits warnings through the standard log::warn! macro whenever something in the plugin lifecycle goes wrong silently. Every message is prefixed with [rust-samp] so it can be grepped quickly in the server log:

[rust-samp] null ICore* in on_load — samp::plugin::omp_core() will return None

The prefix is a single constant in samp/src/macros.rs (SDK_LOG_PREFIX) — do not hardcode the literal in your own logs.

Where they fire

These warnings cover the native Open Multiplayer lifecycle; on SA-MP the SDK does not emit any of them (the corresponding failure modes do not exist in that environment).

Trigger Message Consequence
ICore* was null in comp_on_load null ICore* in on_load — samp::plugin::omp_core() will return None Plugin keeps running; omp_core() returns None.
IPawnComponent missing in comp_on_init IPawnComponent not found in on_init — Pawn natives unavailable Native registration is skipped; Pawn cannot call your natives.
IEventDispatcher<PawnEventHandler> null in comp_on_init null IEventDispatcher<PawnEventHandler> in on_init — on_amx_load/on_amx_unload will not be called Loaded Pawn scripts do not trigger the AMX hooks.
getAmxFunctions() returned 0 even at on_ready getAmxFunctions() returned 0 in on_ready — Pawn natives unavailable Pawn natives are not registered, even after the deferred retry.
on_ready: IPawnComponent could not be queried again on_ready: IPawnComponent not found Same as the previous row — natives are not registered.
ITimersComponent missing while enable_tick() is on ITimersComponent not found — on_tick will not be called Tick callback never fires on Open Multiplayer (SA-MP unaffected).
ITimersComponent::create() returned null failed to create timer on ITimersComponent — on_tick will not be called Same as above; the heap handler is freed before the warning is emitted.

Panic safety

Beyond the warnings, the SDK wraps every callback that crosses the FFI boundary in std::panic::catch_unwind. If a panic escapes the plugin code:

  • Inside a native — the #[native] wrapper catches the panic, logs [<NativeName>] panic in native: <payload> through samp::log::error!, and returns 0 to the AMX VM.
  • Inside an Open Multiplayer vtable callback (comp_on_load, comp_on_init, comp_on_ready, comp_on_free, comp_free, pawn_on_amx_load, pawn_on_amx_unload, tick_handler_timeout, tick_handler_free) — the SDK catches the panic so the server process is not aborted.

Without those guards a panic crossing extern "C" aborts the entire server (Rust 1.71+ guarantee).

Native-call error logging

When a native is declared with a Result/AmxResult return, the generated wrapper logs Err automatically:

[<NativeName>] <Display of the error value>

For argument parsing failures the wrapper logs a more detailed message that includes the positional index and the expected type:

[<NativeName>] failed to parse argument #1 'key' (expected type: AmxString)

This kicks in only in the standard #[native] mode. In raw mode the macro hands the Args over verbatim and parsing is the plugin's responsibility.

Reporting the SDK version

samp::version() returns the CARGO_PKG_VERSION of the rust-samp crate the plugin was built against. Surface it through a Pawn native so bug reports and diagnostic dashboards carry the exact SDK build:

#[native(name = "MyPlugin_GetSdkVersion")]
fn get_sdk_version(_amx: &Amx, out: &mut UnsizedBuffer, size: usize) -> bool {
    let buf = out.into_sized_buffer(size);
    buf.write_str(samp::version()).is_ok()
}

Reading the warnings

All messages flow through whatever log implementation the plugin installed. With the default routing they end up wherever the server sends logprintf (SA-MP) or ICore::logLnU8 (Open Multiplayer). With a custom fern::Dispatch they follow the dispatch chain described in Logging.

A practical filter in the server console:

grep -E '^\[rust-samp\]|^\[<YourNativeName>\]' server_log.txt

Where to look next when something is wrong

  • omp_core() returns None despite running on Open Multiplayer → check the first warning above. The server may have passed a null ICore*, or the plugin is in legacy mode (no ComponentEntryPoint).
  • A Pawn native MyNative(...) is reported as unresolved by the script compiler at runtime → check the IPawnComponent / getAmxFunctions warnings. The dispatcher might not have fired on_amx_load for this script either.
  • on_amx_load is never called → look for the IEventDispatcher<PawnEventHandler> warning.
  • on_tick is never called on Open Multiplayer → look for the ITimersComponent warnings. Make sure samp::plugin::enable_tick() is called inside the constructor block.