Magento – Magento 2.2 – Unable to unserialize value, when editing categories

magento2magento2.2unserialize

The following actions produce the results listed below each action.

If a category has more than 50 items it seems to always happen, categories with less than 50 items will usually work, but will still sometimes trigger the same issue. I had thought perhaps it had to do with the particular items in the category, but I can perform the same actions over and over, and a category which worked previously will then fail, so I don't suspect that individual items are at fault.

When attempting to move a category, does not seem to matter if it has subcategories or not.

There was a category move error.
main.CRITICAL: Unable to unserialize value. {"exception":"[object] (InvalidArgumentException(code: 0): Unable to unserialize value. at /home/___/public_html/vendor/magento/framework/Serialize/Serializer/Json.php:39)"} []

When attempting to change anchor on a category.

Something went wrong while saving the category.
main.CRITICAL: Unable to unserialize value. {"exception":"[object] (InvalidArgumentException(code: 0): Unable to unserialize value. at /home/___/public_html/vendor/magento/framework/Serialize/Serializer/Json.php:39)"} []

If I had just previously tried editing a category, and it failed, changing anchor value will also fail, unless first navigating to another category, and then returning to change anchor.

Something went wrong while saving the category.
main.CRITICAL: Unable to unserialize value. {"exception":"[object] (InvalidArgumentException(code: 0): Unable to unserialize value. at /home/___/public_html/vendor/magento/framework/Serialize/Serializer/Json.php:39)"} []

I've verified that this occurs with all 3rd party extensions disabled.

I recently had another serialization issue, which turned out to be a permissions issue, Magento 2.2: Unable to unserialize value?, but that solution did not have any impact on this issue.

Best Answer

The problem is in /vendor/magento/framework/Serialize/Serializer/Json.php there is a function unserialize($string) which gives You a syntax error if string is serialized.

There is a workaround - You can check if string is serialized and then use serialize($string). Change unserialize to:

public function unserialize($string)
{
    if($this->is_serialized($string))
    {
        $string = $this->serialize($string);
    }
    $result = json_decode($string, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
         throw new \InvalidArgumentException('Unable to unserialize value.');

    }
    return $result;
}

and add function to check if string is serialized:

function is_serialized($value, &$result = null)
{
    // Bit of a give away this one
    if (!is_string($value))
    {
        return false;
    }
    // Serialized false, return true. unserialize() returns false on an
    // invalid string or it could return false if the string is serialized
    // false, eliminate that possibility.
    if ($value === 'b:0;')
    {
        $result = false;
        return true;
    }
    $length = strlen($value);
    $end    = '';
    switch ($value[0])
    {
        case 's':
            if ($value[$length - 2] !== '"')
            {
                return false;
            }
        case 'b':
        case 'i':
        case 'd':
            // This looks odd but it is quicker than isset()ing
            $end .= ';';
        case 'a':
        case 'O':
            $end .= '}';
            if ($value[1] !== ':')
            {
                return false;
            }
            switch ($value[2])
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                    break;
                default:
                    return false;
            }
        case 'N':
            $end .= ';';
            if ($value[$length - 1] !== $end[0])
            {
                return false;
            }
            break;
        default:
            return false;
    }
    if (($result = @unserialize($value)) === false)
    {
        $result = null;
        return false;
    }
    return true;
}

After save fe. category without problem, You can restore class to default and there wont be such problem in future.

Related Topic