Skip to content

All Diagnostics ​

Complete reference of every diagnostic code. For an introduction to how diagnostics work and how to configure them, see the Diagnostics guide.

Error severity ​

CodeDescription
syntaxLua syntax error: code that doesn't parse
safety-limitAnalysis of the file hit an internal limit and stopped early, so some types and diagnostics may be missing. Reported at the top of the file

Warning severity ​

CodeDescription
deprecatedCall to a function marked @deprecated (flavor-aware: suppressed when the API is still live in a flavor the addon targets; see Flavor Filtering)
discard-returnsIgnoring @nodiscard return values
type-mismatchArgument type vs @param mismatch (including generic type arguments, e.g. Container<number> vs Container<boolean>, and string-literal unions, e.g. "x" against "A"|"B"|"C")
return-mismatchReturn type vs @return mismatch
field-type-mismatchField assignment vs @field type mismatch
assign-type-mismatchReassignment vs @type mismatch
generic-constraint-mismatchGeneric argument doesn't satisfy a class, alias, or keyof constraint
param-constraint-mismatchMethod called when the receiver's @requires type-param constraint isn't satisfied
missing-parameterMissing required function arguments
redundant-parameterExtra function arguments
missing-return-valueReturn with fewer values than @return
redundant-return-valueReturn with more values than @return
grouped-return-mismatchReturn values don't match any tuple-union @return case
missing-returnFunction missing return statement
undefined-globalReference to unresolved global name
undefined-fieldReading a nonexistent field on a @class, a module-private table (local p = {}), or a string value (("x").foo, checked against the string library)
need-check-nilField/method access on possibly-nil value (off by default)
nil-indexBracket-indexing a table with a possibly-nil key (off by default)
nil-table-keyTable key type annotation includes nil (table<string?, V>)
access-privateAccessing @field private from outside
access-protectedAccessing @field protected from outside hierarchy (also _-prefixed fields when implicitProtectedPrefix is enabled)
duplicate-indexDuplicate keys in table constructors
redundant-valueExtra values in assignments
unbalanced-assignmentsMore variables than values in assignments, including when a function call returns fewer values than variables assigned
missing-fieldsMissing required fields when constructing @class tables
undefined-doc-classUndefined class name in @class Foo: Parent
undefined-doc-nameUndefined type name in annotations
undefined-doc-param@param name not matching function parameters
duplicate-doc-paramDuplicate @param annotations
duplicate-doc-fieldDuplicate @field annotations
duplicate-doc-aliasDuplicate @alias declarations
doc-field-no-class@field on a non-@class table
doc-func-no-functionFunction-level annotation (@param, @return, etc.) not attached to a function definition
circle-doc-classCircular @class inheritance chains
malformed-annotationUnknown or incomplete ---@ annotations
multi-return-projectionreturns<F> discards extra return values from F
builds-field-not-self@builds-field method uses @return ClassName instead of @return self
unknown-diag-codeUnknown code in @diagnostic directives
unknown-cast-variable@cast of a variable that isn't in scope
duplicate-constructorMultiple @constructor on a single class
constructor-return@constructor with return other than @return self
count-down-loopFor-loop step direction doesn't match start/end
wrong-flavor-apiAPI not available in all declared flavors
secret-arithmeticArithmetic on a value that may be secret (retail)
secret-comparisonComparing a value that may be secret, including as a numeric for bound (retail)
secret-conditionTesting a boolean that may be secret in a condition or and/or (retail)
secret-table-keyUsing a value that may be secret as a table key (retail)
secret-argumentPassing a value that may be secret to an API that rejects secrets from addon code (retail)
secret-accessIndexing (including a method call), calling, or taking the length of a value that may be secret (retail)
redundant-class-genericMethod redeclares class-level @generic
cannot-callCalling a value whose type is not callable
invalid-opArithmetic, concatenation, or </>/<=/>= applied to incompatible types (e.g. + on strings instead of ..), or # on a value that has no length (off by default)
create-globalImplicit global creation
invalid-class-parentInheriting from a non-table type (number, string, literals, etc.)
mixed-enum-values@enum with mixed number/string values or unsupported value types
unknown-callback-eventEvent name passed to a callback registry that was never declared via GenerateCallbackEvents (off by default)
class-shadows-builtinA workspace @class with its own @fields reuses a built-in WoW API class name
redefined-localSame-scope local variable redefinition
duplicate-set-fieldSetting an already-set field on @class tables

Hint severity ​

CodeDescription
return-self-class-nameMethod uses @return ClassName instead of @return self
unused-localUnreferenced local variables (an unused local function reports unused-function instead, which is off by default)
unused-functionUnused function definitions, including local function f() and local f = function() end (off by default)
unused-varargFunction declares ... but never uses it (off by default)
shadowed-localLocal variable shadows an outer-scope variable
inject-fieldSetting undeclared fields on @class tables
unreachable-codeCode after return
code-after-breakCode after break
incomplete-signature-docPartial @param/@return annotations (off by default)
missing-param-annotationNon-file-local function parameter has no @param (off by default)
missing-return-annotationNon-file-local function returns a value but has no @return (off by default)
empty-blockEmpty control flow body
redundant-returnBare return at end of function
trailing-spaceLine ends with whitespace
not-precedencenot x <cmp> y is (not x) <cmp> y
redundant-oror where left side is always truthy (RHS is dead code) (off by default)
redundant-andand where left side is always falsy (RHS is dead code) or always truthy (operator is a no-op) (off by default)
redundant-conditionif/elseif/while condition is provably constant (off by default)
implicit-nil-returnBare return in function with optional @return (off by default)
unknown-param-typeParameter has an unknown or any type (off by default)
unknown-return-typeReturned value has an unknown or any type (off by default)
unknown-local-typeLocal, for, or global variable has an unknown or any type (off by default)
unknown-field-typeClass field has an unknown or any type (off by default)

missing-param-annotation / missing-return-annotation ​

Flag functions that lack @param / @return documentation. Both are off by default. Enable them when you want every public function fully documented:

json
{
  "diagnostics": {
    "enable": ["missing-param-annotation", "missing-return-annotation"]
  }
}

missing-param-annotation fires once per source parameter without a matching @param (the implicit self of a colon method and the conventional _ throwaway are skipped). missing-return-annotation fires once on a function whose body returns a value but has no @return.

Scope: only functions reachable beyond their file are checked. Checked:

  • ✅ Global functions: function GlobalFn() end
  • ✅ Methods/fields on a global table: function GlobalApi.Run() end
  • ✅ Methods/fields on a @class: function Widget:SetValue() end
  • ✅ Methods/fields on the addon namespace: function ns.Module.Process() end
  • ✅ Methods on a local table that is attached to the addon namespace (local M = {}; function M.foo() end; ns.M = M)

Skipped:

  • ❌ local function definitions
  • ❌ Anonymous function literals (local f = function() … end, callback arguments)
  • ❌ Bare function foo() that reassigns a forward-declared local foo
  • ❌ Methods on a file-private local table (local cache = {}; function cache.get() end) that never escapes the file

The escape test for table methods reuses the workspace global scan: a method is "reachable" exactly when the language server registers it as a cross-file symbol, so this stays consistent with go-to-definition and find-references.

This differs from incomplete-signature-doc, which fires only when a signature is partially annotated (and regardless of where the function lives). missing-*-annotation fires even on a completely undocumented function, but only for non-file-local ones. The two can be enabled together; a partially-annotated global will then report under both.

redundant-condition ​

Flags if/elseif/while/repeat...until conditions that are provably always true or always false. Detected patterns:

  • Always-truthy/falsy type: the condition's resolved type is guaranteed truthy (e.g. table, number) or guaranteed falsy (nil).
  • Negation of a constant: not expr where expr is itself always truthy or always falsy (e.g. if not tbl where tbl is a table).
  • Type-incompatible equality: ==/~= between values whose types can never match at runtime (e.g. num == "hello", nonNilVar == nil).
  • Literal-union miss: x == "c" where x is typed as "a"|"b" and "c" is not a member.
  • Two-literal comparison: both sides are concrete literals (e.g. 1 == 2, "a" == "a", 3 < 2).
  • Self-comparison: x < x or x > x (always false; NaN-safe). <=/>=/==/~= self-comparisons are excluded because NaN breaks them.
  • Redundant type() guard: type(x) == "number" where x is already known to be a number (always true) or can never be a number (always false).

Loop idioms (while true, repeat...until false) are not flagged. Conditions referencing variables reassigned inside loops are suppressed to avoid false positives.

unused-function ​

Flags function definitions that are never referenced anywhere in the workspace. Covers top-level global functions, methods defined on tables (e.g. function NS.Method()), and local functions. An unused local function f() reports unused-function, not unused-local.

What counts as "used":

  • Called anywhere in the workspace, including self:Method() calls
  • Referenced as a value (e.g. passed as a callback, stored in a variable or table)
  • Named by a string where the API takes a method name, such as self:ScheduleTimer("OnTick", 1) (parameters typed keyof T)

What is skipped (not flagged):

  • Functions whose name starts with _ (convention for intentionally unused)
  • Functions defined in library files (directories marked with library in .wowluarc.json)
  • Interface methods: if 2+ distinct tables define a method taking self (function T:Method() or function T.Method(self)) with the same name, those methods are treated as a framework callback pattern (duck-typing dispatch). This also covers a method and its override in a subclass. Static functions such as function Module.Reset() are always checked and don't count toward the 2.

Cross-file behavior:

This diagnostic requires a multi-file workspace scan. It compares definitions from all files against references from all files. In single-file mode (e.g. evaluate), only per-file unused functions are detected.

TOC file diagnostics ​

These diagnostics apply to .toc files only. See the TOC Files guide for details.

CodeSeverityDescription
toc-missing-interfaceWarningRequired ## Interface: field is missing
toc-duplicate-headerWarningSame header key appears more than once
toc-unknown-headerHintHeader not in the known catalog and not an X-* custom field
toc-invalid-interfaceErrorInterface value is not a valid numeric version
toc-nonexistent-fileWarningReferenced file does not exist on disk
toc-invalid-valueWarningUnknown game type in AllowLoadGameType / ExcludeLoadGameType (header or per-line), or a header missing its colon

LuaLS compatibility aliases ​

AliasMaps to
invisibleaccess-private, access-protected
param-type-mismatchtype-mismatch
return-type-mismatchreturn-mismatch

Diagnostic codes that LuaLS defines but wowlua_ls has no equivalent for (e.g. lowercase-global, cast-type-mismatch, unused-label) are accepted silently in ---@diagnostic directives - they suppress nothing here, but won't trip unknown-diag-code, so a project that also runs LuaLS can keep its suppressions without noise.