Skip to content
RustPluginGenerator

OnMissionFailed

OnMissionFailed is called after a player's mission attempt is marked failed, right before the server saves their mission state.

Signature
void OnMissionFailed(BaseMission mission, BaseMission.MissionInstance instance, BasePlayer assignee, BaseMission.MissionFailReason failReason)

Parameters

Hook parameters
ParameterTypeDescription
missionBaseMissionThe BaseMission instance that was failed. Use it to read the mission definition and its shortname/identifier properties.
instanceBaseMission.MissionInstanceThe BaseMission.MissionInstance that tracked this player's progress, including objectives and status (now set to Failed).
assigneeBasePlayerThe BasePlayer who owns the mission attempt. Available for chat messages, permission checks, or rewards; null-check it since it may be gone.
failReasonBaseMission.MissionFailReasonA 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

MissionFailureNotifier.cs
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.status is already MissionStatus.Failed and MissionEnded has 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.

Prompt: Explain the failure using the OnMissionFailed hook, with a config file and a permission
Generate this plugin

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