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
- 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.
- 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.
- 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.
- 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.
- 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
- Pin identical CACHE_DRIVER / CACHE_PREFIX / redis host+DB in .env for web and CLI containers before running cache commands.
- Remember forget()===false is a cache miss, not an error — do not alert on it.
- After switching cache drivers, flush the old store once (or accept benign misses on the new one).
- Run `php bin/leantime about` (or dump config) inside the same container/context the command runs in to verify the resolved driver.
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
- Plugin %s is not installed
- Invalid plugin name: %s
- Plugin %s is not enabled
- sprintf($this->language->__($translationKey), ...$values)
AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21).
Data as JSON: /api/errors/5752ab318ee0d677.
Report an issue: GitHub.