Summary
Passing a partial Derma skin to derma.DefineSkin reports 100 missing required fields. Partial skins are valid and idiomatic — the engine resolves anything a skin omits from the Default skin.
There is no Structures/SKIN wiki page (it 404s), so this is entirely custom/class.SKIN.lua and not wiki-fixable.
Why partial skins are correct
derma.DefineSkin gives every non-Default skin a metatable that falls back to the Default skin:
SkinMetaTable.__index = function( self, key )
return DefaultSkin[ key ]
end
...
function DefineSkin( strName, strDescription, strTable )
strTable.Base = strTable.Base or "Default"
if ( strName != "Default" ) then
setmetatable( strTable, SkinMetaTable )
(garrysmod/lua/derma/derma.lua:20-24, 130-140.) If a skin genuinely had to supply all 100 members, that fallback would be unreachable code.
Where the 100 comes from — two separate causes
custom/class.SKIN.lua does two things to the same global:
---@class SKIN declares 49 fields, of which only 3 are optional (Name?, Description?, Base? — exactly the three DefineSkin assigns itself).
- From
SKIN = SKIN or {} onward it implements the Default skin on that same global — 54 SKIN.<field> = ... assignments (PrintName, Author, GwenTexture, the colour and font tables).
Both fold into the class.
Marking the 49 declared fields optional would not fix this. Assignment-derived members are demanded just as strongly. Minimal proof, no GMod annotations involved:
-- lib/decl.lua
---@meta
---@class Thing
---@field declared_req string
---@field declared_opt? string
Thing = Thing or {}
Thing.assigned_field = "x"
Thing.assigned_two = 5
-- use.lua
---@param t Thing
local function take(t) return t end
take({ declared_req = "y" })
warning: Missing required fields in type `Thing`: `assigned_field`, `assigned_two` [missing-fields]
The declared-optional field is correctly ignored; both assigned fields are reported missing.
Repro
Repro zip: repro.zip
derma.DefineSkin("repro_skin", "A partial skin", {
PaintButton = function(self, panel, w, h) end,
})
warning: Missing required fields in type `SKIN`: `Author`, `Colours`, `DermaVersion`, ... (100 fields) ... `tooltip` [missing-fields]
warning: expected `SKIN` but found `{ PaintButton = ... }`. missing member Colours, in table [param-type-mismatch]
It also fires inconsistently
Whether a partial skin is rejected depends only on how it reaches DefineSkin, not on its contents:
SKIN = {}
SKIN.PrintName = "Test"
derma.DefineSkin("a", "via the global", SKIN) -- clean
derma.DefineSkin("b", "via a literal", { PrintName = "Test" }) -- 100 missing fields
Identical content, opposite verdicts. The check only engages on a table literal at the call site — which is also why this isn't more widely reported, since the conventional skin file assigns to the global SKIN and happens to dodge it.
Suggested fix
Both causes need addressing, and the second is a design call I didn't want to guess at:
- Mark the declared
SKIN fields optional. None is mandatory — all are inherited-or-overridable.
- Keep the Default skin's implementation off the
SKIN class. Declaring the interface and then assigning a concrete default implementation onto the same global is what contributes the other 54.
Happy to send a PR for whichever shape you'd prefer for (2) — I held off precisely because it changes what custom/class.SKIN.lua is doing.
Real-world impact
Ecosystem-level rather than a live cost to me — but not because my skin is complete. It defines 51 of the 100 members and none of the Paint* methods; it is thoroughly partial and works correctly, because everything it omits resolves from the Default skin at runtime. It simply routes through the global, so the diagnostic never fires.
Any addon that passes a partial skin as a literal hits it immediately, and the only workarounds are a missing-fields suppression or writing out all 100 members.
Environment
beta @ 07d30c6; annotations gluals-annotations-prerelease @ 2c8a727e
glua_check / glua_ls 1.1.1, Windows 11
Disclosure: this report was researched and written by Claude Code working in my repositories. Every claim in it was reproduced and verified against real workspaces rather than asserted, and the two of us went through the findings together before I filed it. Happy to run further tests or narrow anything down if that would help.
Summary
Passing a partial Derma skin to
derma.DefineSkinreports 100 missing required fields. Partial skins are valid and idiomatic — the engine resolves anything a skin omits from the Default skin.There is no
Structures/SKINwiki page (it 404s), so this is entirelycustom/class.SKIN.luaand not wiki-fixable.Why partial skins are correct
derma.DefineSkingives every non-Default skin a metatable that falls back to the Default skin:(
garrysmod/lua/derma/derma.lua:20-24,130-140.) If a skin genuinely had to supply all 100 members, that fallback would be unreachable code.Where the 100 comes from — two separate causes
custom/class.SKIN.luadoes two things to the same global:---@class SKINdeclares 49 fields, of which only 3 are optional (Name?,Description?,Base?— exactly the threeDefineSkinassigns itself).SKIN = SKIN or {}onward it implements the Default skin on that same global — 54SKIN.<field> = ...assignments (PrintName,Author,GwenTexture, the colour and font tables).Both fold into the class.
Marking the 49 declared fields optional would not fix this. Assignment-derived members are demanded just as strongly. Minimal proof, no GMod annotations involved:
The declared-optional field is correctly ignored; both assigned fields are reported missing.
Repro
Repro zip: repro.zip
It also fires inconsistently
Whether a partial skin is rejected depends only on how it reaches
DefineSkin, not on its contents:Identical content, opposite verdicts. The check only engages on a table literal at the call site — which is also why this isn't more widely reported, since the conventional skin file assigns to the global
SKINand happens to dodge it.Suggested fix
Both causes need addressing, and the second is a design call I didn't want to guess at:
SKINfields optional. None is mandatory — all are inherited-or-overridable.SKINclass. Declaring the interface and then assigning a concrete default implementation onto the same global is what contributes the other 54.Happy to send a PR for whichever shape you'd prefer for (2) — I held off precisely because it changes what
custom/class.SKIN.luais doing.Real-world impact
Ecosystem-level rather than a live cost to me — but not because my skin is complete. It defines 51 of the 100 members and none of the
Paint*methods; it is thoroughly partial and works correctly, because everything it omits resolves from the Default skin at runtime. It simply routes through the global, so the diagnostic never fires.Any addon that passes a partial skin as a literal hits it immediately, and the only workarounds are a
missing-fieldssuppression or writing out all 100 members.Environment
beta@07d30c6; annotationsgluals-annotations-prerelease@2c8a727eglua_check/glua_ls1.1.1, Windows 11Disclosure: this report was researched and written by Claude Code working in my repositories. Every claim in it was reproduced and verified against real workspaces rather than asserted, and the two of us went through the findings together before I filed it. Happy to run further tests or narrow anything down if that would help.