doctrine/orm · error · BadMethodCallException
Undefined method "%s". The method name must start with eithe
Error message
Undefined method "%s". The method name must start with either findBy, findOneBy or countBy!
What it means
BadMethodCallException from EntityRepository::__call() (src/EntityRepository.php:165). EntityRepository intercepts undefined method names and turns them into magic finders, but only for the prefixes findBy*, findOneBy* and countBy*. Any other unreachable method name falls through to this exception naming the method you called.
Source
Thrown at src/EntityRepository.php:165
* @phpstan-param list<mixed> $arguments
*
* @throws BadMethodCallException If the method called is invalid.
*/
public function __call(string $method, array $arguments): mixed
{
if (str_starts_with($method, 'findBy')) {
return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
}
if (str_starts_with($method, 'findOneBy')) {
return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
}
if (str_starts_with($method, 'countBy')) {
return $this->resolveMagicCall('count', substr($method, 7), $arguments);
}
throw new BadMethodCallException(sprintf(
'Undefined method "%s". The method name must start with ' .
'either findBy, findOneBy or countBy!',
$method,
));
}
/** @return class-string<T> */
protected function getEntityName(): string
{
return $this->entityName;
}
public function getClassName(): string
{
return $this->getEntityName();
}
protected function getEntityManager(): EntityManagerInterfaceView on GitHub (pinned to d9b9ff7301)
Solutions
- If you expected a custom method: make sure getRepository() returns your subclass — set repositoryClass in the entity mapping (or use a repository factory).
- For queries, use explicit methods: $repo->findBy(['status' => 'x'], ['createdAt' => 'DESC'], 10) or QueryBuilder.
- Check spelling and casing: prefixes are case-sensitive (findOneBy..., findBy..., countBy...).
- Add the missing finder as a real method on a custom repository class for anything non-trivial.
Example fix
// before
#[Entity]
class Product { }
$repo = $em->getRepository(Product::class);
$repo->findLatest(10); // BadMethodCallException: not findBy/findOneBy/countBy
// after
#[Entity(repositoryClass: ProductRepository::class)]
class Product { }
final class ProductRepository extends EntityRepository {
public function findLatest(int $limit): array {
return $this->createQueryBuilder('p')
->orderBy('p.createdAt', 'DESC')
->setMaxResults($limit)
->getQuery()->getResult();
}
} Defensive patterns
Strategy: type-guard
Validate before calling
$allowed = static fn (string $m): bool =>
str_starts_with($m, 'findBy')
|| str_starts_with($m, 'findOneBy')
|| str_starts_with($m, 'countBy');
if (! method_exists($repo, $method) && ! $allowed($method)) {
throw new BadMethodCallException("{$method} is not a valid magic finder on " . $repo::class);
} Type guard
/** @param class-string<\Doctrine\ORM\EntityRepository> $repoClass */
function canCallRepositoryMethod(string $repoClass, string $method): bool
{
if (method_exists($repoClass, $method)) {
return true;
}
return str_starts_with($method, 'findBy')
|| str_starts_with($method, 'findOneBy')
|| str_starts_with($method, 'countBy');
} Prevention
- Always map entities with repositoryClass so custom methods exist as real methods.
- Prefer explicit findBy()/findOneBy()/count() calls with array criteria over magic names in typed code.
- Remember prefixes are case-sensitive: findOneBy..., findBy..., countBy....
When it happens
Trigger: Calling a method on a repository that does not exist as a real method and does not start with (case-sensitive) 'findBy', 'findOneBy' or 'countBy' — e.g. $repo->findLatest(), $repo->deleteByStatus(), $repo->findbyName() (lowercase 'b'), $repo->findAllByStatus(), or a custom method invoked on the base EntityRepository instead of your subclass.
Common situations: Repository class not configured on the entity (#[Entity(repositoryClass: ...)] missing) so $em->getRepository() returns the generic EntityRepository and your custom methods disappear; typos/casing in magic finder names; expecting magic delete/update/findLatest finders that Doctrine never provided.
Related errors
- The attribute "%s" is repeatable. Call getPropertyAttributeC
- The attribute "%s" is not repeatable. Call getPropertyAttrib
- Uninitialized result set mapping.
- Unable to use access strategy type of [%s] without a Concurr
- Unrecognized access strategy type [%s]
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/f746d035b57c62e6.
Report an issue: GitHub.