Reason 1: Null pointer dereferencing

Usually, a null pointer dereferencing happens when ArduinoJson returns a null whereas the program expects a string.

Example with strcpy()

strcpy() (or its safer equivalent strlcpy()) is a function from the C standard library that copies a string from one place to another. Unfortunately, its behavior is undefined when one of the argument is null.

Here is an example that uses strlcpy() with ArduinoJson:

char name[32];

JsonObject& obj = jsonBuffer.parseObject(input);
strlcpy(name, obj["name"], 32);

This program works fine, except when the value "name" is missing from the object, because obj["name"] returns NULL. The same thing happens if parsing fails.

Here is how to fix this program:

char name[32];

JsonObject& obj = jsonBuffer.parseObject(input);
strlcpy(name, obj["name"] | "N/A", 32);

This snippet uses the | syntax introduced in ArduinoJson 5.12. It is equivalent to the following code:

char name[32];

JsonObject& obj = jsonBuffer.parseObject(input);

const char* jsonName = obj["name"];
if (jsonName)
  strlcpy(name, obj["name"], 32);
else
  strcpy(name, "N/A");

Example with strcmp()

strcmp() is a function from the C standard library that compare two string. Here again, the behavior is undefined if one of the argument is null.

JsonObject& obj = jsonBuffer.parseObject(input);

if (strcmp(root["state"], "OK") == 0) {
  // ...
}

This program works fine, except when the value "state" is missing from the object, because obj["state"] returns NULL. The same thing happens if parsing fails.

Here is how to fix this program:

JsonObject& obj = jsonBuffer.parseObject(input);

if (root["state"] == "OK") {
  // ...
}

Indeed, the JsonVariant returned by root["state"] has a special implementation of the == operator that knows how to compare string safely.

Reason 2: Stack-overflow

A stack overflow happens when you have too many variables in the “stack” memory.

Before reading further, make sure that your target platform does have enough RAM to store the JsonBuffer and possibly the JSON input too:

Once you’re sure that your device has enough RAM, you should move theJsonBuffer to the heap. Just replace your StaticJsonBuffer with a DynamicJsonBuffer.

If your JSON input is stored in the stack, you should move it to the heap too.

For instance, if you have a program like this:

char content[MAX_CONTENT_SIZE];
StaticJsonBuffer<JSON_BUFFER_SIZE> jsonBuffer;

receive(content);
JsonObject& root = jsonBuffer.parseObject(content);

Serial.println(root["name"].asString());

you should transform it like that:

char* content = malloc(MAX_CONTENT_SIZE);
DynamicJsonBuffer jsonBuffer(JSON_BUFFER_SIZE);

receive(content);
JsonObject& root = jsonBuffer.parseObject(content);

Serial.println(root["name"].asString());

free(content);

Reason 3: Incompatible configurations in compilation units

If your program behaves unpredictably, it may be because a different configuration is used in each .ino or .cpp file.

For example, imagine you have two files my_sketch.ino and my_lib.cpp.

The first file starts with:

// File: my_sketch.ino
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>

whereas the second starts with:

// File: my_lib.ino
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>

In that situation, the two compilation units have different sizes for JsonVariant. Since the linker is not able to detect this problem, it will create an executable with some functions using a big JsonVariant and others using small JsonVariant. The executable may work under some conditions but will crash sooner or later.

To fix this bug, you must use the same configuration in all compilation units. A simple way to do that is to share the configuration in a .h file.