laravel/framework · error · UnexpectedValueException

User must implement CanResetPassword interface.

Error message

User must implement CanResetPassword interface.

What it means

Thrown by PasswordBroker::getUser() when the user retrieved by credentials does not implement Illuminate\Contracts\Auth\CanResetPassword. The broker needs this contract to call sendPasswordResetNotification() and to delete/store reset tokens.

Source

Thrown at src/Illuminate/Auth/Passwords/PasswordBroker.php:183

        return $user;
    }

    /**
     * Get the user for the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\CanResetPassword|null
     *
     * @throws \UnexpectedValueException
     */
    public function getUser(#[\SensitiveParameter] array $credentials)
    {
        $credentials = Arr::except($credentials, ['token']);

        $user = $this->users->retrieveByCredentials($credentials);

        if ($user && ! $user instanceof CanResetPasswordContract) {
            throw new UnexpectedValueException('User must implement CanResetPassword interface.');
        }

        return $user;
    }

    /**
     * Create a new password reset token for the given user.
     *
     * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
     * @return string
     */
    public function createToken(CanResetPasswordContract $user)
    {
        return $this->tokens->create($user);
    }

    /**
     * Delete password reset tokens of the given user.

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Make the User model implement Illuminate\Contracts\Auth\CanResetPassword and use the Illuminate\Foundation\Auth\CanResetPassword trait (and Notifiable for the reset notification).
  2. If using a custom model, implement getEmailForPasswordReset() and sendPasswordResetNotification() manually.
  3. Ensure the provider's 'model' config points at the correct class implementing the contract.
  4. Add a static check in tests: assertTrue(is_subclass_of(User::class, CanResetPassword::class)).

Example fix

// before
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    // missing CanResetPassword trait
}

// after
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable; // provides sendPasswordResetNotification
    // Authenticatable already uses CanResetPassword trait
}
Defensive patterns

Strategy: type-guard

Validate before calling

$model = config('auth.providers.users.model');
if (! is_subclass_of($model, \Illuminate\Contracts\Auth\CanResetPassword::class)) {
    throw new RuntimeException("User model [{$model}] must implement CanResetPassword.");
}

Type guard

function userImplementsCanResetPassword(string $modelClass): bool
{
    return is_subclass_of($modelClass, \Illuminate\Contracts\Auth\CanResetPassword::class);
}

Try / catch

try {
    $status = Password::broker()->sendResetLink($credentials);
} catch (\UnexpectedValueException $e) {
    // surface a config error: the User model is missing the contract/trait
    report($e);
}

Prevention

When it happens

Trigger: Calling Password::broker()->sendResetLink([...]) where the matched User model lacks the CanResetPassword implementation (and the CanResetPassword trait that wires getEmailForPasswordReset() and sendPasswordResetNotification()).

Common situations: A custom User model that extends a base other than Illuminate\Foundation\Auth\User; removed the Notifiable + CanResetPassword traits; switched to a non-Eloquent user provider whose model is a plain stdClass.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/f1e51384f08e8a4e.json. Report an issue: GitHub.