Troubleshooting
Common symptoms — TLS failures, version mismatches, JSON decode errors and validation rejections — and how to fix them.
This page collects the failures people hit most often, the symptom you actually see, and the fix. When in doubt,
turn the logger up to Trace (see Logging) to watch URL building, interceptors, and
decoding.
Connection and TLS
Version mismatches
JSON decode errors
The controller is loose about scalar types: the same field can arrive as a number, a quoted string, an empty
string, null, or a word like "auto" / "enabled" depending on firmware. The library smooths this over with
custom unmarshalers in unifi/json.go:
numberOrString— accepts a JSON number or a string (e.g. a radiochannelof36or"auto"), and maps""/nullto the empty string.emptyStringInt— accepts an int that the controller sometimes sends as""; decodes empty/nullto0and marshals0back as""to round-trip cleanly.booleanishString— toleratestrue/false,"enabled"/"disabled","1"/"0", and empty/null, collapsing them to a bool (used by fields likeDevice.LtePoe).
These are applied automatically on the affected fields, so you normally never see them. If you do hit an error
like json: cannot unmarshal string into Go struct field … of type int, it usually means the controller sent a
shape a field doesn't yet tolerate — a sign the generated types have drifted from your firmware. Capture the raw
response (a body-logging HttpRoundTripperProvider, see Interceptors) and
open an issue with the payload.
Don't read resp.Body inside an InterceptResponse — the body is decoded after interceptors run, and draining
it yields a silently zero-valued result. Use an HttpRoundTripperProvider to buffer and restore the body safely.
Validation rejections
In HardValidation mode a write can fail locally before it ever reaches the controller, surfacing a
*unifi.ValidationError. The generated validate tags come from the controller's own API definitions and are
occasionally stricter than a given firmware actually enforces.
If you're confident a value is valid for your controller, you have two mitigations:
- Switch to the default
SoftValidation— the field is logged as a WARN but the request is still sent. - Use
DisableValidationto skip validation entirely (last resort):
c, err := unifi.NewClient(&unifi.ClientConfig{
URL: "https://unifi.example.com",
APIKey: "your-api-key",
ValidationMode: unifi.DisableValidation,
})See Validation for inspecting ValidationError.Messages and registering custom
validators.