25

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.

14 Answers 14

46

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

redis-cli flushall

I hope this helps future users.

  • 1
    Nicely done. This should probably be the accepted answer. – Shawn Abramson May 16 '18 at 15:39
  • 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 at 23:49
  • it works for me – Jared Chu Jul 10 at 4:31
28

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.

  • 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
19

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

  • 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 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
9

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;
}
  • 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 at 12:07
  • What issues you were facing? – MageLearner May 28 at 10:13
  • The issue got fixed. Thanks for asking! – Siva May 28 at 10:15
  • 1
    Grt... Thank you!!! – MageLearner May 28 at 10:16
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.

  • 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 at 20:52
3

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

redis-cli flushall

3

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
1

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

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!

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

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.

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

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

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

For fix - runned

composer update

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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