{"record":{"id":"9a2f6790e04e2b81","repo":"phalcon/cphalcon","slug":"data-for-the-unserializer-must-be-of-type-string-9a2f67","errorCode":null,"errorMessage":"Data for the unserializer must be of type string","messagePattern":"Data for the unserializer must be of type string","errorType":"exception","errorClass":"Phalcon\\Storage\\Serializer\\Exceptions\\InvalidUnserializationInput","httpStatus":null,"severity":"error","filePath":"phalcon/Storage/Serializer/Php.zep","lineNumber":48,"sourceCode":"\n        return this->phpSerialize(this->data);\n    }\n\n    /**\n     * Unserializes data\n     */\n    public function unserialize(mixed data) -> void\n    {\n        var result;\n\n        if (true !== this->isSerializable(data)) {\n            let this->data = data;\n\n            return;\n        }\n\n        if unlikely typeof data != \"string\" {\n            throw new InvalidUnserializationInput();\n        }\n\n        globals_set(\"warning.enable\", false);\n        set_error_handler(\n            function (number, message, file, line) {\n                globals_set(\"warning.enable\", true);\n            },\n            E_NOTICE | E_WARNING\n        );\n\n        let result = this->phpUnserialize(data);\n\n        restore_error_handler();\n\n        if unlikely globals_get(\"warning.enable\") || result === false {\n            let this->isSuccess = false,\n                result          = \"\";\n        } else {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Storage/Serializer/Php.zep#L30-L66","documentation":"Php::unserialize() first applies isSerializable(): null, booleans and numeric values are returned as-is without decoding. Whatever remains must be a serialize() string; a non-string that survives the filter — array, object, resource — throws InvalidUnserializationInput. unserialize() is meant to consume serialize() output, not already-decoded data.","triggerScenarios":"Passing an already-unserialized array to Php::unserialize() (double decoding); feeding an object/resource payload; storage returning an array-shaped payload into the serializer; queue/job code unserializing a message that was json-decoded earlier in the pipeline.","commonSituations":"Manual serializer use in job queues; mixing json_decode() and unserialize() pipelines; round-tripping a value through another serializer before this one; refactors that changed where decoding happens so it now runs twice.","solutions":["Call unserialize() only with strings produced by serialize()","Check before decoding: if (is_string($data)) { $serializer->unserialize($data); } else { $value = $data; }","Rely on the adapter's get() — it serializes/unserializes exactly once per round-trip","When accepting payloads from other systems, detect the format (leading 'a:1:{' vs '{') instead of assuming"],"exampleFix":"// before\n$serializer->unserialize($payload); // $payload already an array -> throws\n\n// after\n$value = is_string($payload) ? $serializer->unserialize($payload) && $serializer->getData() : $payload;","handlingStrategy":"type-guard","validationCode":"if (is_string($payload)) {\n    $serializer->unserialize($payload);\n    $value = $serializer->getData();\n} else {\n    $value = $payload; // already decoded\n}","typeGuard":"function isPhpSerializedString(mixed $data): bool\n{\n    return is_string($data) && preg_match('/^[aObsiNd]:\\d+:/i', $data) === 1;\n}","tryCatchPattern":null,"preventionTips":[" unserialize() only what serialize() produced — one decode per round-trip","Prefer the adapter's get()/set() over manual serializer calls inside app code","Detect payload format (serialize vs json) before decoding mixed-origin data"],"tags":["php","phalcon","serialization","php-serialize","type-error"],"backgroundTag":"type-mismatch","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}