Leantime/leantime · warning · RuntimeException

Plugin %s is not installed

Error message

Plugin %s is not installed

What it means

This warning comes from the artisan command that clears the language cache (app/Command/ClearLanguage.php:60). For every language in the list it calls `Cache::store('installation')->forget('languages.lang_' . $key)`; Laravel's forget() returns false when the key does not exist in that store, and the command prints 'Failed to clear: <key>'. It does not mean the command broke — it means the cache entry was already absent, lives under a different cache driver/prefix than the 'installation' store currently configured, or was never written by this installation.

Source

Thrown at app/Command/EnablePluginCommand.php:38

{
    /**
     * {@inheritdoc}
     */
    protected function configure(): void
    {
        $this->addArgument('plugin', InputArgument::REQUIRED, 'The plugin name');
    }

    /**
     * {@inheritdoc}
     */
    protected function executeCommand(): int
    {
        $name = $this->input->getArgument('plugin');
        $plugin = $this->getPlugin($name);

        if (! isset($plugin->id)) {
            throw new RuntimeException(sprintf('Plugin %s is not installed', $plugin->name));
        }

        if ($plugin->enabled) {
            throw new RuntimeException(sprintf('Plugin %s is already enabled', $plugin->name));
        }

        if (! $this->confirm(sprintf('Enable plugin %s', $plugin->name))) {
            return Command::SUCCESS;
        }

        return $this->plugins->enablePlugin($plugin->id) ? Command::SUCCESS : Command::FAILURE;
    }
}

View on GitHub (pinned to 9a9f49f100)

Solutions

  1. First re-run the command — if the second run warns for the same keys immediately, the CLI and the web runtime are almost certainly using different cache stores; compare `php bin/leantime config:cache`-resolved values vs what the web container uses.
  2. Verify the 'installation' store configuration in app/Core/Configuration/laravelConfig.php (Leantime keeps ALL Laravel config there, not config/*.php) and ensure CACHE_DRIVER/CACHE_PREFIX .env values are identical for web and CLI.
  3. If the keys were simply never written or already expired, ignore the warning: a cache miss is the desired end state — the language list will be rebuilt on next request.
  4. Flush the whole installation store if you need certainty: `php bin/leantime cache:clear` (accepting a broader cache loss), then re-run the language command and expect all keys to 'fail' benignly.
  5. If using redis/database cache in Docker, confirm all containers point at the same redis host and DB index before rerunning.

Example fix

// before — forget() false is reported as a failure even when the entry is simply absent
$result = Cache::store('installation')->forget('languages.lang_' . $key);
if ($result) {
    $this->components->info('Cleared: ' . $key);
} else {
    $this->components->warn('Failed to clear: ' . $key);
}

// after — distinguish 'already absent' from an actual store error
try {
    $existed = Cache::store('installation')->forget('languages.lang_' . $key);
    $this->components->info($existed ? "Cleared: {$key}" : "Not cached (already clear): {$key}");
} catch (\Throwable $e) {
    \Illuminate\Support\Facades\Log::error($e);
    $this->components->warn("Failed to clear: {$key} — " . $e->getMessage());
}
Defensive patterns

Strategy: validation

Validate before calling

// Before running the language cache clear, validate the CLI sees the same cache store the web runtime writes to
$driver = config('cache.stores.installation.driver'); // must match what served requests used
$probeKey = 'languages.lang_' . app()->make(\Leantime\Domain\Setting\Services\Setting::class)?->getSetting('companysettings.language') ?? 'en-US';
$hasEntries = \Illuminate\Support\Facades\Cache::store('installation')->get($probeKey) !== null;
if (! $hasEntries) {
    // cache is already cold or CLI/web stores diverge — expect benign 'Failed to clear' warnings
}

Try / catch

// If you wrap the clear in application code, distinguish 'absent' (fine) from store errors (log)
try {
    $existed = Cache::store('installation')->forget('languages.lang_' . $key);
    logger()->debug($existed ? "cleared {$key}" : "already absent {$key}");
} catch (\Psr\SimpleCache\InvalidArgumentException | \RedisException $e) {
    \Illuminate\Support\Facades\Log::error($e);
    // surface store misconfiguration; do not retry blindly against a wrong store
}

Prevention

When it happens

Trigger: Running the language-clear command when: (1) the language cache entries were already evicted or expired (file/redis cache TTL, manual cache:clear earlier); (2) the cache configuration changed between when the entries were written and now (e.g. switched CACHE_DRIVER from file to redis or the database driver), so forget() looks in an empty store; (3) a CACHE_PREFIX or separate 'installation' store database differs between web requests (which wrote the entries) and the CLI process (different .env, or CLI running as a different user reading another cache path); (4) the language was added after the last cache write, so its key never existed.

Common situations: Dockerized Leantime where the web container uses redis but an exec'd CLI container defaults to the file driver; cache stored on a shared NFS/storage volume with per-user permissions so artisan runs as root and reads a different cache directory; running the command right after `make clear-cache`; multi-instance deployments where instance A holds the warm cache and the command runs on instance B.

Related errors


AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21). Data as JSON: /api/errors/5752ab318ee0d677. Report an issue: GitHub.