41

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.

17 Answers 17

88

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

redis-cli flushall

I hope this helps future users.

| improve this answer | |
  • 2
    Nicely done. This should probably be the accepted answer. – Shawn Abramson May 16 '18 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 '18 at 20:40
  • thanks. i restarted varnish, thinking that would flush it, but this did the trick. – ladle3000 Jan 13 '19 at 23:49
  • it works for me – Jared Chu Jul 10 '19 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; – Mohammed Joraid Oct 3 '19 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.

| improve this answer | |
  • 1
    Its working 100% fine for me. Thanks a lot! – mapaladiya Nov 9 '17 at 10:03
  • 2
    it is not working ... :-( – Arfan Mirza Dec 10 '17 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. – vitoriodachef May 1 '18 at 10:47
  • Fixed the issue in my case – Roy Toledo Jun 5 at 18:59
25

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

| improve this answer | |
  • 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. – vitoriodachef May 1 '18 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. – Mark Shust at M.academy Aug 28 '18 at 13:54
  • @vitoriodachef I'm facing the exact case you have mentioned. Do you have found any solution? – Knight017 Dec 5 '18 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 '18 at 10:50
  • DIdn't work. I had to manually change all entries in the DB from a:0:{} to [] – localhost Oct 7 '19 at 1:08
24

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;
}
| improve this answer | |
  • 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 '19 at 12:07
  • What issues you were facing? – Ajay Gehlod May 28 '19 at 10:13
  • The issue got fixed. Thanks for asking! – Siva May 28 '19 at 10:15
  • 1
    Grt... Thank you!!! – Ajay Gehlod May 28 '19 at 10:16
  • 1
    Thanks @MageLearner , It also working in 2.3.1 after migrate the data from magento 1 to magento 2 – Pradeep Thakur Nov 4 '19 at 8:56
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
| improve this answer | |
4

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

redis-cli flushall

| improve this answer | |
3

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.

| improve this answer | |
  • Hello, I am facing the related issue like this. Can you please look in to this . – Aditya Shah May 14 '18 at 7:07
  • @chunk all my dir are 755,and files are 644, what is the appropriate umask to set? tia – Kris Wen Jun 3 '19 at 20:52
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;
}
| improve this answer | |
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.

| improve this answer | |
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;
} 
| improve this answer | |
  • My issue not fixed..please help me – Muhammad Ahmed Sep 4 '19 at 11:07
1

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!

| improve this answer | |
  • I left crypt key empty during installation expecting a new one to be generated, which clearly does not happen. – Shapeshifter Feb 11 '19 at 21:15
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.

| improve this answer | |
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
| improve this answer | |
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';
| improve this answer | |
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.

| improve this answer | |
0

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;
}
| improve this answer | |
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.

| improve this answer | |

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