rectorphp/rector · error · MissingPrivatePropertyException

Property "$%s" was not found in "%s" class

Error message

Property "$%s" was not found in "%s" class

What it means

PrivatesAccessor reflectively resolves a property for read/write: first on get_class($object), then via get_parent_class() up the hierarchy. If neither level declares it, the search is exhausted and MissingPrivatePropertyException('Property "$%s" was not found in "%s" class') is thrown with the property name and concrete class.

Source

Thrown at src/Util/Reflection/PrivatesAccessor.php:82

    private function resolvePropertyReflection(object $object, string $propertyName): ReflectionProperty
    {
        if (property_exists($object, $propertyName)) {
            $reflection = new ReflectionProperty($object, $propertyName);
            if (\PHP_VERSION_ID < 80100) {
                $reflection->setAccessible(\true);
            }
            return $reflection;
        }
        $parentClass = get_parent_class($object);
        if ($parentClass !== \false) {
            $reflection = new ReflectionProperty($parentClass, $propertyName);
            if (\PHP_VERSION_ID < 80100) {
                $reflection->setAccessible(\true);
            }
            return $reflection;
        }
        $errorMessage = sprintf('Property "$%s" was not found in "%s" class', $propertyName, get_class($object));
        throw new MissingPrivatePropertyException($errorMessage);
    }
}

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Verify the exact name and casing with (new ReflectionClass($object))->getProperty(...) or var_dump(get_class_vars(get_class($object)))
  2. Update the accessor call to the renamed property after upgrading the library
  3. If the state moved to another class in the hierarchy, target that class explicitly instead of the instance's root

Example fix

// before
$value = $privatesAccessor->getPrivateProperty($nodeFactory, 'nameResolver'); // renamed upstream

// after
$value = $privatesAccessor->getPrivateProperty($nodeFactory, 'nameResolverFactory');
Defensive patterns

Strategy: type-guard

Type guard

function privatePropertyExists(object $object, string $propertyName): bool
{
    $class = get_class($object);
    do {
        if (property_exists($class, $propertyName)) {
            return true;
        }
        $class = get_parent_class($class) ?: null;
    } while ($class !== null);

    return false;
}

Try / catch

try {
    $value = $privatesAccessor->getPrivateProperty($object, $propertyName);
} catch (\Rector\Exception\Util\MissingPrivatePropertyException $e) {
    // typo or upstream rename: log and skip the assertion rather than fail hard
    $logger->warning($e->getMessage());
    return null;
}

Prevention

When it happens

Trigger: Calling PrivatesAccessor->getPrivateProperty()/setPrivateProperty() (or the reflection resolver directly) with a misspelled or case-mismatched property name, or against a class whose property was renamed/removed in an upstream release.

Common situations: Rector upgrade tests that poke private state via PrivatesAccessor and break when internals rename a field; typo'd names after refactoring; expecting a property that only exists on a sibling, not the parent chain.

Related errors


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/b23a663f8baf9299. Report an issue: GitHub.