{"record":{"id":"c84bbd52be7f3e91","repo":"getgrav/grav","slug":"cannot-create-new-object-already-exists","errorCode":null,"errorMessage":"Cannot create new object (Already exists)","messagePattern":"Cannot create new object \\(Already exists\\)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Flex/FlexObject.php","lineNumber":714,"sourceCode":"        if ($files && method_exists($this, 'setUpdatedMedia')) {\n            $this->setUpdatedMedia($files);\n        }\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see FlexObjectInterface::create()\n     */\n    public function create(?string $key = null)\n    {\n        if ($key) {\n            $this->setStorageKey($key);\n        }\n\n        if ($this->exists()) {\n            throw new RuntimeException('Cannot create new object (Already exists)');\n        }\n\n        return $this->save();\n    }\n\n    /**\n     * @param string|null $key\n     * @return FlexObject|FlexObjectInterface\n     */\n    public function createCopy(?string $key = null)\n    {\n        $this->markAsCopy();\n\n        return $this->create($key);\n    }\n\n    /**\n     * @param UserInterface|null $user","sourceCodeStart":696,"sourceCodeEnd":732,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Flex/FlexObject.php#L696-L732","documentation":"FlexObject::create() is strictly for inserting new objects: it refuses to proceed when exists() reports that the storage key is already taken, throwing 'Cannot create new object (Already exists)'. The object was loaded with (or explicitly given) a key that already exists in storage, so an insert would silently overwrite it.","triggerScenarios":"Loading an existing object ($directory->getObject($key)) and calling create() instead of save()/update(); calling create('existing-key') with a manually chosen key; race where two requests create the same slug/key concurrently and the second loses.","commonSituations":"Duplicate submission of an admin 'create' form (double click / refresh) reusing the same generated key; import scripts that re-run without wiping storage; frontend code that creates objects with user-supplied unique fields.","solutions":["Use save() for existing objects (update path) and create() only for new ones: $object->exists() ? $object->save() : $object->create();","Check existence first: if ($directory->getObject($key)) { /* handle duplicate: error or pick new key */ }.","For user-supplied keys, generate a unique variant (append -1, -2) before create().","Guard double submissions in the form layer (unique tokens) rather than relying on the storage error."],"exampleFix":"// before\n$object->create($key); // throws when key taken\n\n// after\nif ($object->exists()) {\n    $object->save(); // update existing\n} else {\n    $object->create($key);\n}","handlingStrategy":"validation","validationCode":"// Decide insert vs update before touching storage\nif ($object->exists()) {\n    $object->save();          // update path\n} else {\n    $object->create($key);    // insert path\n}\n\n// For user-chosen keys, ensure uniqueness first:\nwhile ($directory->getObject($key) !== null) {\n    $key = $baseKey . '-' . $i++;\n}","typeGuard":null,"tryCatchPattern":"try {\n    $object->create();\n} catch (\\Grav\\Framework\\Flex\\Exception\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'Already exists')) {\n        $object->save(); // degrade to update, or surface a duplicate error\n    }\n}","preventionTips":["Reserve create() for brand-new objects; use save() after loading existing ones.","Check $object->exists() before create().","Generate unique keys for duplicate-prone submissions (double-click, form refresh).","Add CSRF/unique tokens to creation forms."],"tags":["grav","flex","duplicate-key","crud"],"backgroundTag":"duplicate-key-conflict","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}