{"record":{"id":"70df334028f32fc7","repo":"vlucas/phpdotenv","slug":"expected-either-an-instance-of-s-or-a-class-strin","errorCode":null,"errorMessage":"Expected either an instance of %s or a class-string implementing %s","messagePattern":"Expected either an instance of (.+?) or a class-string implementing (.+?)","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Repository/RepositoryBuilder.php","lineNumber":144,"sourceCode":"        return (new ReflectionClass($name))->implementsInterface(AdapterInterface::class);\n    }\n\n    /**\n     * Creates a repository builder with the given reader added.\n     *\n     * Accepts either a reader instance, or a class-string for an adapter. If\n     * the adapter is not supported, then we silently skip adding it.\n     *\n     * @param \\Dotenv\\Repository\\Adapter\\ReaderInterface|string $reader\n     *\n     * @throws \\InvalidArgumentException\n     *\n     * @return \\Dotenv\\Repository\\RepositoryBuilder\n     */\n    public function addReader($reader)\n    {\n        if (!(\\is_string($reader) && self::isAnAdapterClass($reader)) && !($reader instanceof ReaderInterface)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    'Expected either an instance of %s or a class-string implementing %s',\n                    ReaderInterface::class,\n                    AdapterInterface::class\n                )\n            );\n        }\n\n        $optional = Some::create($reader)->flatMap(static function ($reader) {\n            return \\is_string($reader) ? $reader::create() : Some::create($reader);\n        });\n\n        $readers = \\array_merge($this->readers, \\iterator_to_array($optional));\n\n        return new self($readers, $this->writers, $this->immutable, $this->allowList);\n    }\n\n    /**","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/vlucas/phpdotenv/blob/416df702837983f8d5ff48c9c3fee4f5f57b980b/src/Repository/RepositoryBuilder.php#L126-L162","documentation":"RepositoryBuilder::addReader() (src/Repository/RepositoryBuilder.php:144) accepts exactly two kinds of argument: an instance of ReaderInterface, or a class-string naming a class that implements AdapterInterface (verified via class_exists + ReflectionClass::implementsInterface, src/Repository/RepositoryBuilder.php:120). Anything else — an object implementing neither, a string that is not an adapter class (including interface names or non-existent classes), or a scalar — throws InvalidArgumentException. When a class-string passes the check, the builder later calls its static create(); if it is unsupported it is silently skipped, not an error.","triggerScenarios":"RepositoryBuilder::createWithDefaultAdapters()->addReader(new SomeAdapter()) where SomeAdapter implements neither ReaderInterface nor AdapterInterface; addReader('App\\Env\\CustomReader') where that class implements only ReaderInterface (a string must be an AdapterInterface class — pass a ReaderInterface as an instance instead); addReader(ReaderInterface::class) (an interface, so class_exists fails); addReader('Dotenv\\Adapter\\EnvConstAdapter') (old v2/v3 namespace string that no longer exists).","commonSituations":"Upgrading from phpdotenv 2/3/4 where adapters were registered as differently-named class-strings; wiring a custom reader and passing its class name out of habit; copy-pasted snippets from outdated docs/bundles.","solutions":["Pass a ReaderInterface implementation as an INSTANCE: ->addReader(new CustomReader()).","Or pass the class-string of a class implementing AdapterInterface that has a static create() (e.g. EnvConstAdapter::class, ArrayAdapter::class).","Verify before calling: class_exists($s) && is_subclass_of($s, AdapterInterface::class).","Check the namespace matches the installed major version (v5 adapters live in Dotenv\\Repository\\Adapter\\).","If you implement your own adapter class-string, give it a public static create(): Option — otherwise $reader::create() fatals even after the guard passes."],"exampleFix":"// before\n$builder = RepositoryBuilder::createWithDefaultAdapters()\n    ->addReader(\\App\\Env\\CustomReader::class); // implements only ReaderInterface -> throws\n\n// after\n$builder = RepositoryBuilder::createWithDefaultAdapters()\n    ->addReader(new \\App\\Env\\CustomReader());","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"use Dotenv\\Repository\\Adapter\\AdapterInterface;\nuse Dotenv\\Repository\\Adapter\\ReaderInterface;\n\nfunction isAcceptableReader(ReaderInterface|string $candidate): bool\n{\n    if ($candidate instanceof ReaderInterface) {\n        return true;\n    }\n\n    return is_string($candidate)\n        && class_exists($candidate)\n        && is_subclass_of($candidate, AdapterInterface::class);\n}","tryCatchPattern":null,"preventionTips":["Instance => ReaderInterface; class-string => AdapterInterface. Never mix the two forms.","A class-string adapter must expose public static create() returning a PhpOption\\Option.","After upgrading phpdotdotenv, grep for old adapter namespaces before wiring the builder.","Let DI inject typed instances instead of config strings when possible."],"tags":["repository","adapter","invalid-argument","php","dotenv"],"backgroundTag":"invalid-argument-exception","analyzedSha":"416df702837983f8d5ff48c9c3fee4f5f57b980b","analyzedAt":"2026-08-21T01:19:52.946Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}