OnMissionFailed
OnMissionFailed is called after a player's mission attempt is marked failed, right before the server saves their mission state.
void OnMissionFailed(BaseMission mission, BaseMission.MissionInstance instance, BasePlayer assignee, BaseMission.MissionFailReason failReason)Parameters
| Parameter | Type | Description |
|---|---|---|
mission | BaseMission | The BaseMission instance that was failed. Use it to read the mission definition and its shortname/identifier properties. |
instance | BaseMission.MissionInstance | The BaseMission.MissionInstance that tracked this player's progress, including objectives and status (now set to Failed). |
assignee | BasePlayer | The BasePlayer who owns the mission attempt. Available for chat messages, permission checks, or rewards; null-check it since it may be gone. |
failReason | BaseMission.MissionFailReason | A BaseMission.MissionFailReason enum explaining why the mission failed (for example death, leaving the area, or a timer expiring). |
Return value
The return value is ignored: the patcher uses continue behavior and the original BaseMission.MissionFailed method returns void. Returning a value or null changes nothing about the failure.
How it works
The hook fires inside BaseMission.MissionFailed, after the analytics event is recorded, the instance status is set to Failed, and MissionEnded runs. It is the last point before MissionsDirty flushes the player's mission data, so the failure is already final when your plugin sees it. The call only happens for missions that reach this failure path, not for ones that complete successfully.
The mission argument identifies which contract failed, instance gives you that player's progress record, assignee is the player, and failReason tells you the cause as a BaseMission.MissionFailReason value. You can act on the failure: notify the player, grant a partial reward, log it, or start a cooldown. All parameters are concrete game types, so Oxide always dispatches calls; you do not gain anything by narrowing the parameter types here.
Because it runs on every mission failure, keep work inside it light. Send chat lines or write data, but do not spawn heavy effects or iterate large server collections.
Example plugin: Notify players about failed missions
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Mission Failure Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Notifies players and tracks failures when a mission attempt fails.")]
public class MissionFailureNotifier : RustPlugin
{
private const string PermUse = "missionfailurenotifier.use";
private PluginConfig config;
private readonly Dictionary<ulong, int> failureCounts = new Dictionary<ulong, int>();
private class PluginConfig
{
[JsonProperty("Announce failures in chat")]
public bool AnnounceInChat { get; set; } = true;
[JsonProperty("Mention session failure count")]
public bool MentionCount { get; set; } = true;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Failed"] = "Mission {0} failed: {1}",
["FailedCount"] = "Mission {0} failed: {1} (session failures: {2})",
["Reason.Dead"] = "you died",
["Reason.Unknown"] = "mission aborted"
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermUse, this);
}
private void OnMissionFailed(BaseMission mission, BaseMission.MissionInstance instance, BasePlayer assignee, BaseMission.MissionFailReason failReason)
{
if (assignee == null || !assignee.IsConnected) return;
var userId = assignee.UserIDString;
if (permission.UserHasPermission(userId, PermUse) && config.AnnounceInChat)
{
var reasonText = Msg("Reason." + failReason, userId);
var missionName = "mission";
if (mission != null && !string.IsNullOrEmpty(mission.name))
missionName = mission.name;
if (config.MentionCount)
{
int count;
failureCounts.TryGetValue(assignee.userID.Get(), out count);
count = Mathf.Min(count + 1, 999);
failureCounts[assignee.userID.Get()] = count;
assignee.ChatMessage(Msg("FailedCount", userId, missionName, reasonText, count));
}
else
{
assignee.ChatMessage(Msg("Failed", userId, missionName, reasonText));
}
}
}
private void Unload()
{
failureCounts.Clear();
}
}
}The plugin reacts to failed missions by sending the assignee a localized chat line that names the fail reason. The reason text is looked up from the enum through a Lang key, and the mission's name is included so players know which contract ended. A per-player failure count is kept in memory and can be mentioned in the message; the stored count is capped at 999 to avoid excessive values. The hook only messages players who have the missionfailurenotifier.use permission and only when Announce failures in chat is enabled. The player must be connected, and the mission must not be null before its name is used. Apart from the optional count, the plugin sends a single chat message per failure.
Common use cases
Explain the failure
Send the player a chat line describing the fail reason so they understand whether they died, ran out of time, or left the mission area.
Partial rewards
Grant a small consolation payment via an Economics reference when a mission fails, scaled by how far the instance progressed.
Failure tracking
Persist per-player failure counts to oxide/data so you can build leaderboards or gate weekly mission limits.
Pitfalls
Status is already set
By the time the hook runs,
instance.statusis alreadyMissionStatus.FailedandMissionEndedhas fired. Do not try to flip the mission back to active here.Does not fire on success
Missions that complete call the success path instead. Handle completed missions with the success hook rather than expecting a failReason of success here.
Keep it cheap
This runs for every failed attempt on the server. Avoid heavy loops or repeated data writes; batch persistence or use a timer if you need to save.
Build a plugin with OnMissionFailed
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnMissionFailed
Generate