laravel/framework · error · InvalidArgumentException

The environment property must be set and cannot be empty.

Error message

The environment property must be set and cannot be empty.

What it means

Thrown by the Bind container attribute when the environments argument, after filtering, resolves to an empty array. The constructor accepts a string, UnitEnum, or array and runs array_filter to drop falsy values; if everything was filtered out (e.g. an empty string, null, or [null]) the attribute is meaningless and Laravel refuses to register an environment-conditional binding.

Source

Thrown at src/Illuminate/Container/Attributes/Bind.php:43

     */
    public array $environments = [];

    /**
     * Create a new attribute instance.
     *
     * @param  class-string  $concrete
     * @param  non-empty-array<int, \UnitEnum|non-empty-string>|non-empty-string|\UnitEnum  $environments
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(
        string $concrete,
        string|array|UnitEnum $environments = ['*'],
    ) {
        $environments = array_filter(is_array($environments) ? $environments : [$environments]);

        if ($environments === []) {
            throw new InvalidArgumentException('The environment property must be set and cannot be empty.');
        }

        $this->concrete = $concrete;

        $this->environments = array_map(
            fn ($environment) => enum_value($environment),
            $environments,
        );
    }
}

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Pass at least one non-empty environment string or a UnitEnum case, e.g. #[Bind(Concrete::class, 'production')] or #[Bind(Concrete::class, ['testing','staging'])].
  2. Use the wildcard default #[Bind(Concrete::class)] (defaults to ['*']) if you want the binding to apply everywhere.
  3. If environments come from config/env, validate they are non-empty before using the attribute, or fall back to the wildcard.

Example fix

// before
#[Bind(PaymentGateway::class, environments: config('app.bind_env'))]
class Gateway {}

// after
#[Bind(PaymentGateway::class, environments: 'production')]
class Gateway {}
Defensive patterns

Strategy: validation

Validate before calling

$envs = array_filter((array) $environments);
if ($envs === []) {
    throw new \InvalidArgumentException('environments must contain at least one non-empty value');
}
new \Illuminate\Container\Attributes\Bind($concrete, $envs);

Type guard

function isValidEnvironmentList(string|array|\UnitEnum $envs): bool
{
    return array_filter(is_array($envs) ? $envs : [$envs]) !== [];
}

Try / catch

try {
    #[Bind(Concrete::class, environments: $dynamicEnv)]
    class X {}
} catch (\InvalidArgumentException $e) {
    // fall back to wildcard binding
}

Prevention

When it happens

Trigger: Declaring #[Bind(SomeClass::class, environments: '')] or #[Bind(SomeClass::class, environments: [null, ''])] on a class. Also #[Bind(SomeClass::class, environments: [])] after the constructor wraps a scalar and filters it.

Common situations: Reading environments from a config or env variable that resolves to an empty string; passing a dynamic variable that is sometimes empty; misconfiguring a multi-environment binding where all entries were filtered.

Related errors


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