{"record":{"id":"82607aeb8bcbbbbe","repo":"knadh/listmonk","slug":"subscribers-invalidjson","errorCode":null,"errorMessage":"subscribers.invalidJSON","messagePattern":"subscribers\\.invalidJSON","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/campaigns.go","lineNumber":743,"sourceCode":"\t\t\tc.Messenger = \"email\"\n\t\t} else {\n\t\t\treturn c, errors.New(a.i18n.Ts(\"campaigns.fieldInvalidMessenger\", \"name\", c.Messenger))\n\t\t}\n\t}\n\n\tcamp := models.Campaign{Body: c.Body, TemplateBody: tplTag}\n\tif err := c.CompileTemplate(a.manager.TemplateFuncs(&camp)); err != nil {\n\t\treturn c, errors.New(a.i18n.Ts(\"campaigns.fieldInvalidBody\", \"error\", err.Error()))\n\t}\n\n\tif len(c.Headers) == 0 {\n\t\tc.Headers = make([]map[string]string, 0)\n\t}\n\n\t// Validate and initialize attribs.\n\tif c.Attribs != nil {\n\t\tif _, err := json.Marshal(c.Attribs); err != nil {\n\t\t\treturn c, errors.New(a.i18n.T(\"subscribers.invalidJSON\"))\n\t\t}\n\t}\n\n\tif len(c.ArchiveMeta) == 0 {\n\t\tc.ArchiveMeta = json.RawMessage(\"{}\")\n\t}\n\n\tif c.ArchiveSlug.String != \"\" {\n\t\t// Format the slug to be alpha-numeric-dash.\n\t\ts := strings.ToLower(c.ArchiveSlug.String)\n\t\ts = strings.TrimSpace(reSlug.ReplaceAllString(s, \" \"))\n\t\ts = regexpSpaces.ReplaceAllString(s, \"-\")\n\n\t\tc.ArchiveSlug = null.NewString(s, true)\n\t} else {\n\t\t// If there's no slug set, set it to NULL in the DB.\n\t\tc.ArchiveSlug.Valid = false\n\t}","sourceCodeStart":725,"sourceCodeEnd":761,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/cmd/campaigns.go#L725-L761","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"attribs\": \"{\\\"plan\\\": pro}\"}\n// after\n{\"attribs\": {\"plan\": \"pro\"}}","handlingStrategy":"validation","validationCode":"// round-trip attribs through JSON to guarantee serializability\nfunction sanitizeAttribs(attribs) {\n  if (attribs == null) return null; // omit the field entirely\n  return JSON.parse(JSON.stringify(attribs));\n}","typeGuard":"function isJSONObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    JSON.stringify(v) !== undefined;\n}","tryCatchPattern":"try {\n  await api.createCampaign({ attribs, ...rest });\n} catch (e) {\n  if (e.message.includes('invalidJSON')) {\n    throw new Error('attribs could not be serialized as JSON — check for invalid values');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["json","validation","campaign","attributes"],"backgroundTag":"invalid-json-payload","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}