cakephp/cakephp · error · InvalidArgumentException

Could not find primary key value for source entity

Error message

Could not find primary key value for source entity

What it means

replaceLinks() rewrites the junction-table links for a saved source entity, which requires the entity's binding key values. If extracting the binding key columns from the source entity yields empty/null values, the links cannot be identified and this error is thrown.

Solutions

  1. Persist (save) the source entity before calling replaceLinks so its primary/binding keys are populated
  2. Verify the entity contains the binding key fields (check getBindingKey() against the entity)
  3. Fix the bindingKey configuration on the association if it names wrong columns

Example fix

// before
$article = $this->Articles->newEntity($data);
$this->Articles->Tags->replaceLinks($article, $tags);
// after
$article = $this->Articles->save($this->Articles->patchEntity($this->Articles->newEmptyEntity(), $data));
$this->Articles->Tags->replaceLinks($article, $tags);
Defensive patterns

Strategy: validation

Validate before calling

$bindingKey = (array)$articles->Tags->getBindingKey();
if (count(array_filter($entity->extract($bindingKey))) !== count($bindingKey)) {
    $entity = $articles->save($entity); // or refetch
}

Try / catch

try { $assoc->replaceLinks($entity, $targets); } catch (InvalidArgumentException $e) { $entity = $assoc->getSource()->save($entity); $assoc->replaceLinks($entity, $targets); }

Prevention

When it happens

Trigger: Calling $articles->Tags->replaceLinks($entity, $targets) where $entity is new or lacks the bindingKey properties (e.g. composite binding key only partially set).

Common situations: Calling replaceLinks on an unpersisted entity; entity was created with newEntity() without an id; composite primary key entity saved without all key columns.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of cakephp/cakephp@1128eba9b0 (2026-09-12). Data as JSON: /api/errors/26e63564c8c1e54e. Report an issue: GitHub.

Appendix: source

Thrown at src/ORM/Association/BelongsToMany.php:1215

     * `$article->get('tags')` will contain only `[$tag1, $tag3]` at the end
     *
     * @param \Cake\Datasource\EntityInterface $sourceEntity an entity persisted in the source table for
     *   this association
     * @param array $targetEntities list of entities from the target table to be linked
     * @param array<string, mixed> $options list of options to be passed to the internal `save`/`delete` calls
     *   when persisting/updating new links, or deleting existing ones
     * @throws \InvalidArgumentException if non persisted entities are passed or if
     *   any of them is lacking a primary key value
     * @return bool success
     */
    public function replaceLinks(EntityInterface $sourceEntity, array $targetEntities, array $options = []): bool
    {
        $bindingKey = (array)$this->getBindingKey();
        $primaryValue = $sourceEntity->extract($bindingKey);

        if (count(Hash::filter($primaryValue)) !== count($bindingKey)) {
            $message = 'Could not find primary key value for source entity';
            throw new InvalidArgumentException($message);
        }

        return $this->junction()->getConnection()->transactional(
            function () use ($sourceEntity, $targetEntities, $primaryValue, $options) {
                $junction = $this->junction();
                $target = $this->getTarget();

                /** @var array<string> $foreignKey */
                $foreignKey = (array)$this->getForeignKey();
                $assocForeignKey = (array)$junction->getAssociation($target->getAlias())->getForeignKey();
                $prefixedForeignKey = array_map($junction->aliasField(...), $foreignKey);

                $junctionPrimaryKey = (array)$junction->getPrimaryKey();
                $junctionQueryAlias = $junction->getAlias() . '__matches';
                $keys = [];
                $matchesConditions = [];
                /** @var string $key */
                foreach (array_merge($assocForeignKey, $junctionPrimaryKey) as $key) {

View on GitHub (pinned to 1128eba9b0)