Skip to content
RustPluginGenerator
CS0246Oxide + Carbon

Fix CS0246: 'JsonPropertyAttribute' could not be found (Oxide)

The plugin uses [JsonProperty] on config fields but never adds using Newtonsoft.Json, so the compiler cannot resolve the attribute. Add that using directive at the top of the file and the error disappears.

Server console
Error while compiling KitCooldowns:
[1] The type or namespace name 'JsonPropertyAttribute' could not be found (are you missing a using directive or an assembly reference?) at 13:14

Why it happens

Oxide plugins mark config fields with [JsonProperty("Readable key")] so Config.ReadObject and Config.WriteObject map JSON keys to C# fields. That attribute lives in the Newtonsoft.Json namespace, which is not imported by default in a new .cs file. If you paste a config class from another plugin, follow a snippet, or copy a config example and forget the top-of-file usings, the compiler sees JsonProperty as an unknown name and reports CS0246 with the message about 'JsonPropertyAttribute' (the attribute name is expanded to its full class name). The same line also appears when the attribute is spelled with a wrong casing or when a second attribute name is mistyped next to it, because the failing token is reported at its line and column. This is a pure compile error: Oxide drops the file from the batch, so the plugin never loads until the using directive is present and the file compiles.

How to fix it

  1. Open the plugin .cs file in oxide/plugins and look at the line and column from the console message. In the example it points to the first [JsonProperty] on the config class.
  2. Check the using block at the top of the file. Add using Newtonsoft.Json; next to using Oxide.Core.Plugins; and the other namespaces.
  3. Save the file. Oxide reloads the file automatically; you can also run oxide.reload KitCooldowns.
  4. A clean build prints Loaded plugin KitCooldowns v1.0.0 by RustPluginGenerator and no Error while compiling line. If CS0246 persists, the column points to a different identifier, so read the actual name in the message.
  5. Confirm the config was written by checking oxide/config/KitCooldowns.json; the keys should match the strings inside [JsonProperty(...)].

Before and after

BeforeFails
using System;
using System.Collections.Generic;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("KitCooldowns", "RustPluginGenerator", "1.0.0")]
    [Description("Gives players a kit on a cooldown using a chat command")]
    public class KitCooldowns : RustPlugin
    {
        private class PluginConfig
        {
            [JsonProperty("Cooldown seconds")]
            public int CooldownSeconds { get; set; } = 300;

            [JsonProperty("Kit item shortname")]
            public string KitItem { get; set; } = "scrap";

            [JsonProperty("Kit item amount")]
            public int KitAmount { get; set; } = 100;
        }

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> lastUse = new Dictionary<ulong, DateTime>();

        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>
            {
                ["Claimed"] = "You received {0} x {1}.",
                ["Cooldown"] = "You must wait {0} seconds before claiming again."
            }, this);
        }

        [ChatCommand("kit")]
        private void CmdKit(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;

            DateTime last;
            if (lastUse.TryGetValue(player.userID, out last))
            {
                double remaining = config.CooldownSeconds - (DateTime.UtcNow - last).TotalSeconds;
                if (remaining > 0)
                {
                    player.ChatMessage(string.Format(lang.GetMessage("Cooldown", this, player.UserIDString), (int)remaining));
                    return;
                }
            }

            var item = ItemManager.CreateByName(config.KitItem, config.KitAmount);
            if (item == null) return;

            player.GiveItem(item);
            lastUse[player.userID] = DateTime.UtcNow;
            player.ChatMessage(string.Format(lang.GetMessage("Claimed", this, player.UserIDString), config.KitAmount, config.KitItem));
        }
    }
}
AfterFixed
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("KitCooldowns", "RustPluginGenerator", "1.0.0")]
    [Description("Gives players a kit on a cooldown using a chat command")]
    public class KitCooldowns : RustPlugin
    {
        private class PluginConfig
        {
            [JsonProperty("Cooldown seconds")]
            public int CooldownSeconds { get; set; } = 300;

            [JsonProperty("Kit item shortname")]
            public string KitItem { get; set; } = "scrap";

            [JsonProperty("Kit item amount")]
            public int KitAmount { get; set; } = 100;
        }

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> lastUse = new Dictionary<ulong, DateTime>();

        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>
            {
                ["Claimed"] = "You received {0} x {1}.",
                ["Cooldown"] = "You must wait {0} seconds before claiming again."
            }, this);
        }

        [ChatCommand("kit")]
        private void CmdKit(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;

            DateTime last;
            if (lastUse.TryGetValue(player.userID, out last))
            {
                double remaining = config.CooldownSeconds - (DateTime.UtcNow - last).TotalSeconds;
                if (remaining > 0)
                {
                    player.ChatMessage(string.Format(lang.GetMessage("Cooldown", this, player.UserIDString), (int)remaining));
                    return;
                }
            }

            var item = ItemManager.CreateByName(config.KitItem, config.KitAmount);
            if (item == null) return;

            player.GiveItem(item);
            lastUse[player.userID] = DateTime.UtcNow;
            player.ChatMessage(string.Format(lang.GetMessage("Claimed", this, player.UserIDString), config.KitAmount, config.KitItem));
        }
    }
}

FAQ

Do I need Newtonsoft.Json for every config class?

Only if you use [JsonProperty] to name the keys. Without the attribute, Config.ReadObject<T>() still works and uses the field names as keys, but underscores and casing are less readable in the JSON file. Most plugins keep the attribute and the using directive.

The console says 'JsonPropertyAttribute' but my code says JsonProperty. Why?

C# attributes are classes derived from System.Attribute, and the compiler prints the expanded type name in CS0246. [JsonProperty] and [JsonPropertyAttribute] are the same thing, so the message still points at your attribute usage.

Does Carbon need a different namespace?

No. Carbon loads the same Oxide plugin file and ships Newtonsoft.Json, so using Newtonsoft.Json; resolves the same way. Only the config and log folder paths differ (carbon/configs, carbon/logs).

Why does the plugin not load even though other plugins work?

Each file compiles on its own. A file with a compile error is removed from the batch and the rest still load, but the broken plugin is skipped entirely. Fix the using directive, save, and the console will show the Loaded plugin line.

Different error?

Paste the full console output and get the cause and a fix in plain English.

Open the error explainer

Skip the debugging

Generated plugins are compiled before you get them, and compiler errors are fixed automatically.

Generate a plugin

2 free plugins a month, no card.

Last updated Oct 26, 2026.

Get plugins that compile first time

Generate