knadh/listmonk · error
subscribers.invalidJSON
Error message
subscribers.invalidJSON
What it means
When campaign attributes (c.Attribs) are provided, validateCampaignFields probes serializability with json.Marshal(c.Attribs); failure returns subscribers.invalidJSON. It ensures the attributes blob is valid JSON-shaped data before it is stored, reusing the subscriber-side i18n key.
Source
Thrown at cmd/campaigns.go:743
c.Messenger = "email"
} else {
return c, errors.New(a.i18n.Ts("campaigns.fieldInvalidMessenger", "name", c.Messenger))
}
}
camp := models.Campaign{Body: c.Body, TemplateBody: tplTag}
if err := c.CompileTemplate(a.manager.TemplateFuncs(&camp)); err != nil {
return c, errors.New(a.i18n.Ts("campaigns.fieldInvalidBody", "error", err.Error()))
}
if len(c.Headers) == 0 {
c.Headers = make([]map[string]string, 0)
}
// Validate and initialize attribs.
if c.Attribs != nil {
if _, err := json.Marshal(c.Attribs); err != nil {
return c, errors.New(a.i18n.T("subscribers.invalidJSON"))
}
}
if len(c.ArchiveMeta) == 0 {
c.ArchiveMeta = json.RawMessage("{}")
}
if c.ArchiveSlug.String != "" {
// Format the slug to be alpha-numeric-dash.
s := strings.ToLower(c.ArchiveSlug.String)
s = strings.TrimSpace(reSlug.ReplaceAllString(s, " "))
s = regexpSpaces.ReplaceAllString(s, "-")
c.ArchiveSlug = null.NewString(s, true)
} else {
// If there's no slug set, set it to NULL in the DB.
c.ArchiveSlug.Valid = false
}View on GitHub (pinned to 670c01717d)
Solutions
- Send attribs as a proper JSON object (e.g. {"key": "value"}) not a string.
- Sanitize values client-side: JSON.stringify then re-parse to guarantee valid JSON before the API call.
- Remove non-UTF8 characters and non-finite numbers from the payload.
- Omit attribs entirely (null) if not needed, since the check only runs when Attribs != nil.
Example fix
// before
{"attribs": "{\"plan\": pro}"}
// after
{"attribs": {"plan": "pro"}} Defensive patterns
Strategy: validation
Validate before calling
// round-trip attribs through JSON to guarantee serializability
function sanitizeAttribs(attribs) {
if (attribs == null) return null; // omit the field entirely
return JSON.parse(JSON.stringify(attribs));
} Type guard
function isJSONObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v) &&
JSON.stringify(v) !== undefined;
} Try / catch
try {
await api.createCampaign({ attribs, ...rest });
} catch (e) {
if (e.message.includes('invalidJSON')) {
throw new Error('attribs could not be serialized as JSON — check for invalid values');
}
throw e;
} Prevention
- Send attribs as a JSON object, never a pre-stringified string or binary.
- Strip NaN/Infinity and non-UTF8 data before serialization.
- Round-trip payloads through JSON.parse(JSON.stringify(...)) in JS clients.
- Omit attribs when not needed.
When it happens
Trigger: CreateCampaign/UpdateCampaign/TestCampaign where the attribs field cannot be marshalled — non-UTF8 bytes, values of unsupported types injected via a custom client, or cyclic/odd payloads from programmatic clients.
Common situations: Clients sending attribs as a raw string instead of a JSON object, NaN/Infinity numbers, or binary data smuggled into the JSON payload by an upstream serializer.
Related errors
- campaigns.fieldInvalidFromEmail
- campaigns.fieldInvalidName
- campaigns.fieldInvalidSubject
- campaigns.fieldInvalidSendAt
- campaigns.fieldInvalidListIDs
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/82607aeb8bcbbbbe.
Report an issue: GitHub.