w7corp/easywechat · error · InvalidArgumentException
The factory must return a %s instance.
Error message
The factory must return a %s instance.
What it means
Application::getOAuth() supports a custom OAuth provider factory via setOAuthFactory(). After invoking the factory it requires an instance of Overtrue\Socialite\Contracts\ProviderInterface; anything else (another class, null, a builder object) triggers InvalidArgumentException. The guard exists because the rest of the OAuth flow (redirect(), userFromCode()) only works against the Socialite provider contract.
Source
Thrown at src/OfficialAccount/Application.php:179
/**
* @throws InvalidArgumentException
*/
public function getOAuth(): SocialiteProviderInterface
{
if (! $this->oauthFactory) {
$this->oauthFactory = fn (self $app): SocialiteProviderInterface => (new WeChat(
[
'client_id' => $this->getAccount()->getAppId(),
'client_secret' => $this->getAccount()->getSecret(),
'redirect_url' => $this->config->get('oauth.redirect_url'),
]
))->scopes((array) $this->config->get('oauth.scopes', ['snsapi_userinfo']));
}
$provider = call_user_func($this->oauthFactory, $this);
if (! $provider instanceof SocialiteProviderInterface) {
throw new InvalidArgumentException(sprintf(
'The factory must return a %s instance.',
SocialiteProviderInterface::class
));
}
return $provider;
}
public function getTicket(): JsApiTicketInterface|RefreshableJsApiTicketInterface
{
if (! $this->ticket) {
$this->ticket = new JsApiTicket(
appId: $this->getAccount()->getAppId(),
secret: $this->getAccount()->getSecret(),
cache: $this->getCache(),
httpClient: $this->getClient(),
stable: $this->config->get('use_stable_access_token', false),
);View on GitHub (pinned to f0cf0a8b83)
Solutions
- Return the provider object from the factory closure, e.g. return (new WeChat([...]))->scopes([...])
- Implement Overtrue\Socialite\Contracts\ProviderInterface on the custom class
- Type the closure's return (fn (): ProviderInterface => ...) so PHP/static analysis rejects mismatches before runtime
Example fix
// before
$app->setOAuthFactory(fn ($app) => new MyCustomSso()); // not a Socialite provider
// after
$app->setOAuthFactory(
fn ($app) => (new \Overtrue\Socialite\Providers\WeChat([
'client_id' => $app->getAccount()->getAppId(),
'client_secret' => $app->getAccount()->getSecret(),
'redirect_url' => 'https://example.com/oauth/callback',
]))->scopes(['snsapi_userinfo'])
); Defensive patterns
Strategy: type-guard
Type guard
function isSocialiteProvider(mixed $provider): bool
{
return $provider instanceof \Overtrue\Socialite\Contracts\ProviderInterface;
}
$provider = ($factory)($app);
if (! isSocialiteProvider($provider)) {
throw new \UnexpectedValueException(
'OAuth factory returned '.get_debug_type($provider).', expected ProviderInterface.'
);
} Try / catch
try {
$provider = $app->getOAuth();
} catch (\EasyWeChat\Kernel\Exceptions\InvalidArgumentException $e) {
throw new \UnexpectedValueException('Custom OAuth factory must return a Socialite provider; got '.get_debug_type($factoryResult), 0, $e);
} Prevention
- Type the factory closure's return (fn (): ProviderInterface => ...) so mismatches fail statically
- Keep custom providers as small adapters implementing ProviderInterface
- Assert instanceof against the interface (not a concrete class) in tests
When it happens
Trigger: setOAuthFactory(fn ($app) => new MyProvider(...)) where MyProvider does not implement ProviderInterface; a factory closure that forgets its return statement; a factory returning a config array or builder instead of the provider object.
Common situations: Swapping in a custom or extended WeChat provider; refactoring a closure so the return disappears; test factories returning mocks of the wrong interface.
Related errors
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/800c70b1a312b3a8.
Report an issue: GitHub.