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(): EntityManagerInterface

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. If you expected a custom method: make sure getRepository() returns your subclass — set repositoryClass in the entity mapping (or use a repository factory).
  2. For queries, use explicit methods: $repo->findBy(['status' => 'x'], ['createdAt' => 'DESC'], 10) or QueryBuilder.
  3. Check spelling and casing: prefixes are case-sensitive (findOneBy..., findBy..., countBy...).
  4. 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

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


AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21). Data as JSON: /api/errors/f746d035b57c62e6. Report an issue: GitHub.