getgrav/grav · error · RuntimeException

Cannot create new object (Already exists)

Error message

Cannot create new object (Already exists)

What it means

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.

Source

Thrown at system/src/Grav/Framework/Flex/FlexObject.php:714

        if ($files && method_exists($this, 'setUpdatedMedia')) {
            $this->setUpdatedMedia($files);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     * @see FlexObjectInterface::create()
     */
    public function create(?string $key = null)
    {
        if ($key) {
            $this->setStorageKey($key);
        }

        if ($this->exists()) {
            throw new RuntimeException('Cannot create new object (Already exists)');
        }

        return $this->save();
    }

    /**
     * @param string|null $key
     * @return FlexObject|FlexObjectInterface
     */
    public function createCopy(?string $key = null)
    {
        $this->markAsCopy();

        return $this->create($key);
    }

    /**
     * @param UserInterface|null $user

View on GitHub (pinned to 6040efed04)

Solutions

  1. Use save() for existing objects (update path) and create() only for new ones: $object->exists() ? $object->save() : $object->create();
  2. Check existence first: if ($directory->getObject($key)) { /* handle duplicate: error or pick new key */ }.
  3. For user-supplied keys, generate a unique variant (append -1, -2) before create().
  4. Guard double submissions in the form layer (unique tokens) rather than relying on the storage error.

Example fix

// before
$object->create($key); // throws when key taken

// after
if ($object->exists()) {
    $object->save(); // update existing
} else {
    $object->create($key);
}
Defensive patterns

Strategy: validation

Validate before calling

// Decide insert vs update before touching storage
if ($object->exists()) {
    $object->save();          // update path
} else {
    $object->create($key);    // insert path
}

// For user-chosen keys, ensure uniqueness first:
while ($directory->getObject($key) !== null) {
    $key = $baseKey . '-' . $i++;
}

Try / catch

try {
    $object->create();
} catch (\Grav\Framework\Flex\Exception\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'Already exists')) {
        $object->save(); // degrade to update, or surface a duplicate error
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/c84bbd52be7f3e91. Report an issue: GitHub.