43

Running into issues on a site running Magento 2.2.0-rc3.0/PHP 7.0.23

The following issue occurs with all 3rd party extensions either enabled or disabled.

When adding an item to comparison from category or product page or submitting a review from product page we get the following error in the browser:

1 exception(s):
Exception #0 (InvalidArgumentException): Unable to unserialize value.

Exception #0 (InvalidArgumentException): Unable to unserialize value.
#0 /home/___/public_html/app/code/Magento/Theme/Controller/Result/MessagePlugin.php(157): Magento\Framework\Serialize\Serializer\Json->unserialize('[{\\"type\\":\\"su...')
#1 /home/___/public_html/app/code/Magento/Theme/Controller/Result/MessagePlugin.php(135): Magento\Theme\Controller\Result\MessagePlugin->getCookiesMessages()
#2 /home/___/public_html/app/code/Magento/Theme/Controller/Result/MessagePlugin.php(84): Magento\Theme\Controller\Result\MessagePlugin->getMessages()
#3 /home/___/public_html/lib/internal/Magento/Framework/Interception/Interceptor.php(146): Magento\Theme\Controller\Result\MessagePlugin->afterRenderResult(Object(Magento\Framework\View\Result\Page\Interceptor), Object(Magento\Framework\View\Result\Page\Interceptor), Object(Magento\Framework\App\Response\Http\Interceptor))
#4 /home/___/public_html/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\Framework\View\Result\Page\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Response\Http\Interceptor))
#5 /home/___/public_html/generated/code/Magento/Framework/View/Result/Page/Interceptor.php(26): Magento\Framework\View\Result\Page\Interceptor->___callPlugins('renderResult', Array, Array)
#6 /home/___/public_html/lib/internal/Magento/Framework/App/Http.php(139): Magento\Framework\View\Result\Page\Interceptor->renderResult(Object(Magento\Framework\App\Response\Http\Interceptor))
#7 /home/___/public_html/lib/internal/Magento/Framework/App/Bootstrap.php(256): Magento\Framework\App\Http->launch()
#8 /home/___/public_html/index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
#9 {main}

The error does not go away unless you clear cookies, in particular, the mage-messages cookie. enter image description here

Any assistance in troubleshooting these errors is appreciated.

2

17 Answers 17

94

I was able to resolve this issue by Flushing my Redis Cache from the CLI

redis-cli flushall

I hope this helps future users.

9
  • 2
    Nicely done. This should probably be the accepted answer. May 16, 2018 at 15:39
  • 1
    It seems to not always be the solution. In my case I do not even use redis (yet)
    – Alex
    Sep 5, 2018 at 20:40
  • thanks. i restarted varnish, thinking that would flush it, but this did the trick.
    – ladle3000
    Jan 13, 2019 at 23:49
  • it works for me
    – Jared Chu
    Jul 10, 2019 at 4:31
  • 3
    This helped me while upgrading from 2.2.9 to 2.3.2. I got the error when I executed php bin/magento setup:upgrade; Oct 3, 2019 at 8:01
36

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 (not json but php serialization).

There is a workaround - you can check if string is serialized (vs json-encoded) 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.

4
  • 1
    Its working 100% fine for me. Thanks a lot!
    – mapaladiya
    Nov 9, 2017 at 10:03
  • 2
    it is not working ... :-( Dec 10, 2017 at 1:30
  • Check what happens if the value a:0:{} is passed. Go line by line. What happens if the result of unserialize is passed to a strong typed method that is expecting an array? You might want to change your answer. May 1, 2018 at 10:47
  • Fixed the issue in my case
    – Roy Toledo
    Jun 5, 2020 at 18:59
31

In my case, I patched as follows to unserialize a serialized string: File: /vendor/magento/framework/Serialize/Serializer/Json.php

Find:

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

replace by:

public function unserialize($string)
{
    $result = json_decode($string, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        if(false !== @unserialize($string)){
            return unserialize($string);
        }
        throw new \InvalidArgumentException('Unable to unserialize value.');
    }
    return $result;
}
13
  • I have tried this but it's not working as expected. Have anyone tried this and if got it works, please help me
    – Siva
    May 20, 2019 at 12:07
  • What issues you were facing? May 28, 2019 at 10:13
  • The issue got fixed. Thanks for asking!
    – Siva
    May 28, 2019 at 10:15
  • 1
    Grt... Thank you!!! May 28, 2019 at 10:16
  • 1
    Thanks @MageLearner , It also working in 2.3.1 after migrate the data from magento 1 to magento 2 Nov 4, 2019 at 8:56
26

Do not edit core files for solution. Override following way Just put following line in di.xml inside etc directory

<preference for="Magento\Framework\Serialize\Serializer\Json" type="Namespace\ModuleName\Serialize\Serializer\Json" />

And inside Namespace\ModuleName\Serialize\Serializer Directory: file Json.php

<?php
namespace Namespace\ModuleName\Serialize\Serializer;



class Json extends \Magento\Framework\Serialize\Serializer\Json
{


    /**
     * {@inheritDoc}
     * @since 100.2.0
     */
    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;
    }


    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;
    }
}

Works perfectly

7
  • 2
    The implementation is flawed. What happens if the value a:0:{} is passed to Json:unserialize method? Is it the desired behaviour? What is the point of the result variable in is_serialized method? It's not returned and it doesn't have impact over anything as at method call no variable is passed as second argument. May 1, 2018 at 10:51
  • This should be the accepted solution, and is much better than the post above to edit the file directly in vendor. More than likely, you'll have to run the setup upgrade task locally and then again on staging/production, so it'll have to persist environments and vendor/ directory is an artifact created at build time. Aug 28, 2018 at 13:54
  • @vitoriodachef I'm facing the exact case you have mentioned. Do you have found any solution?
    – Knight017
    Dec 5, 2018 at 4:50
  • I have used following function to decide private function isSerialized($value) { return (boolean) preg_match('/^((s|i|d|b|a|O|C):|N;)/', $value); }
    – Knight017
    Dec 20, 2018 at 10:50
  • DIdn't work. I had to manually change all entries in the DB from a:0:{} to []
    – localhost
    Oct 7, 2019 at 1:08
7

After flushing Redis the issue has sorted. Thank you Craig for the solution.

I'm using port 6379 for cache, so I run command :

redis-cli -p 6379 flushall
4

It turned out to be a permissions issue, where magento was setting permissions for generated files that were restricted on this server.

Solved by creating magento_umask file in the root directory with the appropriate umask for the server.

See http://devdocs.magento.com/guides/v2.2/install-gde/install/post-install-umask.html for additional details.

2
  • Hello, I am facing the related issue like this. Can you please look in to this . May 14, 2018 at 7:07
  • @chunk all my dir are 755,and files are 644, what is the appropriate umask to set? tia Jun 3, 2019 at 20:52
4

It's mostly related with Redis cache, so try to flush out this with simple command in your SSH

redis-cli flushall

2

Sameers' answer above worked for me although i had to use different code in the block.

public function serialize($data)
{
    $result = json_encode($data);
    if (false === $result) {
        throw new \InvalidArgumentException('Unable to serialize value.');
    }
    return $result;
}

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;
}

/**
 * {@inheritDoc}
 */
public function unserialize($string)
{
    if($this->is_serialized($string))
        {
        $result = $this->serialize($string);
        }
    $result = json_decode($string, true);

    return $result;
}
2

I personally found this issue reared it's head running the command:

php bin/magento setup:upgrade

After a migration. I found out that I was missing the "crypt" hash key in src/app/etc/env.php:

<?php
return [
    'backend' => [
        'frontName' => 'admin'
    ],
    'crypt' => [
        'key' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    ],

    ...

Ensure this is not empty and preferably matches your projects' other environments!

1
  • I left crypt key empty during installation expecting a new one to be generated, which clearly does not happen. Feb 11, 2019 at 21:15
2

For what it is worth, the above approach did not work for me and I found that an empty string was being passed to the unserialize function. Doing a check and returning an empty array solved my issue. It's a hack, but was able to move on.

public function unserialize($string)
{
    if(empty($string)){
        return [];
    }

    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;
}
2
  • Your solution has worked for me. Any idea about why its generating this issue?
    – Camit1dk
    Feb 24, 2021 at 4:49
  • No idea. But felt it is was most likely due to a Magento 1 migration.
    – qrider
    Jun 29, 2021 at 18:23
1

I was getting the error in a CMS page in front end.

It was the Magento widget code in the CMS Page Content that was causing problem(which I copied from another source). I deleted the widget code and inserted the same widget using the Insert Widget button in the CMS Page edit screen and it worked.

The above process formatted the widget code differently and it made the error go away.

1

ROOT directory 1. public_html/vendor/magento/framework/Serialize/Serializer/Json.php

Download JSON.php https://gist.github.com/manojind/9f18bbecaeb3e2bbfb056a634ade62a2

2. Just replace below function (unserialize) and add new function OR just download the attached file and replace with default

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;
}

3. Add new function :

function is_serialized($value, &$result = null)
{

    if (!is_string($value))
    {
        return false;
    }

    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':
                       $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;
} 
1
  • My issue not fixed..please help me Sep 4, 2019 at 11:07
0

I found out that whole serialized data cannot be fit into a database MySQL table column with TEXT data type.
I just found the column flag_data value of system_config_snapshot line is trimmed.

I had to change it to MEDIUMTEXT for this column flag.flag_data.

0

Was same error. When tried to update database(ver 2.2.6) with fresh code (ver 2.3.2).

For fix - runned

composer update
0

This is not the best way to run sql directly but i did that for save my time. Just run this query

ALTER TABLE flag MODIFY flag_data LONGTEXT;
UPDATE flag SET flag_data = '{"system":"","scopes":"","themes":""}' WHERE flag_code = 'config_hash';
UPDATE flag SET flag_data = '{}' WHERE flag_code = 'system_config_snapshot';
0

If you are on 2.3.0 or greater you will want to use the solution provided by MageLearner. The older way with case statements is obsolete. If you don't use MageLearner's solution on 2.3.0 or greater; you will hit all sorts of problems with viewing order data and configurable products.

0

I'm on Magento 2.3.3 with Mailchimp installed. When I updated my Mailchimp to the latest version the error disappeared: https://github.com/mailchimp/mc-magento2/releases/tag/102.3.41 You can also see in there release notes that they fix this issue.

Not the answer you're looking for? Browse other questions tagged or ask your own question.