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
- Persist (save) the source entity before calling replaceLinks so its primary/binding keys are populated
- Verify the entity contains the binding key fields (check getBindingKey() against the entity)
- 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
- Only call replaceLinks on persisted entities
- Confirm bindingKey fields exist on the entity
- Check ->isNew() before replaceLinks
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
- All primary key value(s) are needed for updating
- Cannot insert row in
- Cannot insert row, some of the primary key values are…
- Cannot update ` `. The ` ` has no primary key.
- Deleting requires all primary key values.
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)