{"record":{"id":"352909f6d7c37fb6","repo":"getgrav/grav","slug":"cannot-save-data-string-required","errorCode":null,"errorMessage":"Cannot save data, string required","messagePattern":"Cannot save data, string required","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/File/File.php","lineNumber":30,"sourceCode":"namespace Grav\\Framework\\File;\n\nuse RuntimeException;\nuse function is_string;\n\n/**\n * Class File\n * @package Grav\\Framework\\File\n */\nclass File extends AbstractFile\n{\n    /**\n     * {@inheritdoc}\n     * @see FileInterface::save()\n     */\n    public function save($data): void\n    {\n        if (!is_string($data)) {\n            throw new RuntimeException('Cannot save data, string required');\n        }\n\n        parent::save($data);\n    }\n}\n","sourceCodeStart":12,"sourceCodeEnd":36,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/File/File.php#L12-L36","documentation":"Grav\\Framework\\File\\File is the raw, format-agnostic file handler: its save() demands a PHP string and throws for any other type before delegating to AbstractFile::save(). Structured data (arrays/objects) belongs to DataFile or a formatter-aware class, which encodes automatically. This is a strict API-contract error — nothing was written.","triggerScenarios":"Calling save(['key' => 'value']), save(123) or save($object) on a File instance: using File where DataFile was intended; passing an un-cast scalar from config; refactors that changed the payload from string to array without changing the class.","commonSituations":"Copy-pasting DataFile examples against a File object; switching persistence from serialized strings to structured arrays; saving computed ints/floats/bools without an explicit cast.","solutions":["Cast the payload: $file->save((string) $data).","Switch to Grav\\Framework\\File\\DataFile (or the filesystem/formatter factory) when saving structured data.","Add a type check at the call site (is_string($data)) or a native string type hint in your own wrapper."],"exampleFix":"// before\n$file = new \\Grav\\Framework\\File\\File($path);\n$file->save(['foo' => 'bar']); // RuntimeException: Cannot save data, string required\n\n// after (structured data -> DataFile via formatter-aware factory)\n$file = $filesystem->file($path); // DataFile bound to the right formatter\n$file->save(['foo' => 'bar']);\n\n// or raw string\n$file->save((string) json_encode(['foo' => 'bar']));","handlingStrategy":"type-guard","validationCode":"if (!is_string($data)) {\n    $data = (string) $data; // or switch to a DataFile for structured data\n}\n$file->save($data);","typeGuard":"function isRawStringPayload(mixed $data): bool\n{\n    return is_string($data);\n}","tryCatchPattern":"try {\n    $file->save($data);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'string required')) {\n        $file->save((string) $data);\n    }\n}","preventionTips":["Choose File (raw bytes) vs DataFile (structured) deliberately at design time.","Declare string parameter types in your own wrappers so misuse fails at the boundary.","Cast scalars explicitly before saving."],"tags":["php","grav","type-error","file-save","api-misuse"],"backgroundTag":"invalid-argument-type","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}