sebastianbergmann/phpunit · error · IncompatibleReturnValueException
Method %s may not return value of type %s, its declared retu
Error message
Method %s may not return value of type %s, its declared return type is "%s"
What it means
Thrown by ensureTypeOfReturnValues() when a value passed to willReturn() is not accepted by the doubled method's declared return type (ConfigurableMethod::mayReturn()). PHPUnit only checks this when it knows the method's return type (configuredMethod !== null); values that pass PHP's own type checks are required, so this is a static type-compatibility guard before the stub is ever called.
Source
Thrown at src/Framework/MockObject/Runtime/AbstractInvocationImplementation.php:299
return $_valueMap;
}
/**
* @param array<mixed> $values
*
* @throws IncompatibleReturnValueException
*/
private function ensureTypeOfReturnValues(array $values): void
{
$configuredMethod = $this->configuredMethod();
if ($configuredMethod === null) {
return;
}
foreach ($values as $value) {
if (!$configuredMethod->mayReturn($value)) {
throw new IncompatibleReturnValueException(
$configuredMethod,
$value,
);
}
}
}
}
View on GitHub (pinned to f123cdb2a2)
Solutions
- Return a value of the declared type: cast, construct the right class, or use a matching literal.
- For nullable return types, willReturn(null) is fine; otherwise pick a real value.
- When the value is computed, use ->willReturnCallback(fn () => ...) and let PHP's type system verify the callback's return.
- If the production signature changed, update the stub to the new return type instead of suppressing the check.
Example fix
// before
/** @return int */
public function count(): int;
$stub->method('count')->willReturn('42');
// after
$stub->method('count')->willReturn(42); Defensive patterns
Strategy: type-guard
Validate before calling
$rm = new ReflectionMethod(C::class, 'getCount'); $value = 42; $ok = $value === null && $rm->getReturnType()->allowsNull(); // or simply assert before stubbing: assert($value instanceof ($rm->getReturnType()->getName()) || !is_object($value));
Type guard
function valueMatchesReturnType(ReflectionMethod $m, mixed $value): bool
{
$type = $m->getReturnType();
if ($type === null) {
return true;
}
if ($value === null) {
return $type->allowsNull();
}
return match ($type->getName()) {
'int' => is_int($value),
'string' => is_string($value),
'bool' => is_bool($value),
'float' => is_float($value) || is_int($value),
default => $value instanceof $type->getName(),
};
} Try / catch
try {
$mock->method('getCount')->willReturn($value);
} catch (PHPUnit\Framework\MockObject\IncompatibleReturnValueException $e) {
// coerce or rebuild the value to satisfy the declared return type
} Prevention
- Mirror the production signature exactly when stubbing; update stubs whenever return types are tightened.
- Enable PHPStan/Psalm on the tests directory — willReturn() type errors are caught statically in IDEs with the phpunit plugin.
- For computed values prefer willReturnCallback() and let the return type of the callback enforce correctness.
When it happens
Trigger: ->method('getCount')->willReturn('5') on a method declared : int; ->willReturn(null) on a non-nullable return type; ->willReturn(new Foo) where the method returns Bar; willReturnConsecutiveCalls() entries where one element violates the type.
Common situations: Tests written before a return type was added to production code (tightening types in refactors breaks them); stubbing with string literals for int/float methods under strict comparisons; returning null from a stub for a method made non-nullable; copying expectations between similar classes with different return types.
Related errors
- getrusage() returned non-integer values for "%s" and/or "%s"
- Comparison method %s::%s() does not declare bool return type
- %s is not an accepted argument type for comparison method %s
- Cannot double method with invalid name "%s"
- Cannot double using a method list that contains duplicates:
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/e83d43c856763b42.
Report an issue: GitHub.