{"record":{"id":"6ebd7bd227b203c8","repo":"symfony/symfony","slug":"cache-key-s-has-non-serializable-s-value-6ebd7b","errorCode":null,"errorMessage":"Cache key \"%s\" has non-serializable \"%s\" value.","messagePattern":"Cache key \"(.+?)\" has non-serializable \"(.+?)\" value\\.","errorType":"exception","errorClass":"Symfony\\Component\\Cache\\Exception\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php","lineNumber":223,"sourceCode":"        return $now < $expiresAt;\n    }\n\n    protected function doSave(array $values, int $lifetime): array|bool\n    {\n        $ok = true;\n        $expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';\n        $allowCompile = self::isSupported();\n\n        foreach ($values as $key => $value) {\n            unset($this->values[$key]);\n            $isStaticValue = true;\n            if (null === $value) {\n                $value = \"'N;'\";\n            } elseif (\\is_object($value) || \\is_array($value)) {\n                try {\n                    $value = VarExporter::export($value, $isStaticValue);\n                } catch (\\Exception $e) {\n                    throw new InvalidArgumentException(\\sprintf('Cache key \"%s\" has non-serializable \"%s\" value.', $key, get_debug_type($value)), 0, $e);\n                }\n            } elseif (\\is_string($value)) {\n                // Wrap \"N;\" in a closure to not confuse it with an encoded `null`\n                if ('N;' === $value) {\n                    $isStaticValue = false;\n                }\n                $value = var_export($value, true);\n            } elseif (!\\is_scalar($value)) {\n                throw new InvalidArgumentException(\\sprintf('Cache key \"%s\" has non-serializable \"%s\" value.', $key, get_debug_type($value)));\n            } else {\n                $value = var_export($value, true);\n            }\n\n            $encodedKey = rawurlencode($key);\n\n            if ($isStaticValue) {\n                $value = \"return [{$expiry}, {$value}];\";\n            } elseif ($this->appendOnly) {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/symfony/symfony/blob/698e28026c22cf35d032cdb6e800db48b1535790/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php#L205-L241","documentation":"PhpFilesAdapter::doSave() (PhpFilesAdapter.php:219-224) serializes object/array values via VarExporter into PHP files that OPcache compiles. If VarExporter::export throws (resource inside, closure, unsupported internal object), InvalidArgumentException is raised, naming the offending key and value type.","triggerScenarios":"Calling `$pool->save($item)` (or saveDeferred/commit) where the item's value is an object/array containing a resource, closure, or VarExporter-incompatible internal class.","commonSituations":"Caching entities that hold open file handles, GD images, PDO statements, or SplFileInfo; caching DTOs with closure-based computed properties.","solutions":["Convert non-exportable fields to scalars/arrays before saving.","Make objects implement `__serialize`/`__unserialize` and keep them free of resources/closures.","Switch to an adapter that uses PHP's native serialize() (e.g. FilesystemAdapter) if you must store resource-bearing objects — though resources still won't survive."],"exampleFix":"// before\n$item->set($pdoStatement); // PDOStatement\n$pool->save($item);\n\n// after\n$item->set($pdoStatement->fetchAll(\\PDO::FETCH_ASSOC));\n$pool->save($item);","handlingStrategy":"validation","validationCode":"foreach ($values as $k => $v) {\n    if (is_object($v) || is_array($v)) {\n        try { \\Symfony\\Component\\VarExporter\\VarExporter::export($v); }\n        catch (\\Throwable $e) { throw new \\RuntimeException(sprintf('%s not exportable', $k), 0, $e); }\n    }\n}\n$pool->save($item);","typeGuard":"function isExportable(mixed $value): bool\n{\n    if (is_resource($value) || $value instanceof \\Closure) { return false; }\n    try { \\Symfony\\Component\\VarExporter\\VarExporter::export($value); return true; }\n    catch (\\Throwable) { return false; }\n}","tryCatchPattern":"try {\n    $pool->save($item);\n} catch (\\Symfony\\Component\\Cache\\Exception\\InvalidArgumentException $e) {\n    // convert value to a safe representation and retry, or log and skip\n}","preventionTips":["Cache DTOs composed only of scalars/arrays/serializable objects.","Close file/stream handles before storing their data.","Add VarExporter::export smoke tests for cached value types."],"tags":["cache","serialization","varexporter","php-files-adapter"],"analyzedSha":"698e28026c22cf35d032cdb6e800db48b1535790","analyzedAt":"2026-08-06T23:40:49.025Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}