Leantime/leantime · info · RuntimeException
Plugin %s is not enabled
Error message
Plugin %s is not enabled
What it means
This is an informational skip message from the maintenance command behind orphaned-file cleanup (app/Command/CleanupOrphanedFilesCommand.php:102). While iterating user-uploaded files in storage, the command computes `time() - $storage->lastModified($file)` and refuses to delete anything younger than 86400 seconds (24 hours). The warn() output is expected, protective behavior: young files are given a grace period because they may still be referenced by an in-flight upload, an unsaved editor session, or a queue job that has not linked them to their parent entity yet.
Source
Thrown at app/Command/DisablePluginCommand.php:42
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 not enabled', $plugin->name));
}
if (! $this->confirm(sprintf('Disable plugin %s', $plugin->name))) {
return Command::SUCCESS;
}
return $this->plugins->disablePlugin($plugin->id) ? Command::SUCCESS : Command::FAILURE;
}
}
View on GitHub (pinned to 9a9f49f100)
Solutions
- Treat the message as success, not failure: the file WILL be eligible for deletion once it is older than 24 hours — simply wait and let the next scheduled run process it.
- If you must verify deletion logic now, test against a file whose mtime you age manually: `touch -d '2 days ago' storage/app/uploads/<file>` then re-run the command.
- Confirm you are not accidentally running in --dry-run mode, which reports the same skip for young files while never deleting anything.
- If the skip list never shrinks day over day, check clock synchronization (NTP) between the app server and the storage/NFS/S3 volume so lastModified() is not in the future.
- Only if you fully understand the concurrency risk, adjust the hardcoded 86400 threshold in the command to a smaller window and redeploy.
Example fix
// before — hard-coded 24h grace period, constant buried in the condition
if ($fileAge < 86400) { // 24 hours
$this->warn("Skipping {$file} - file is less than 24 hours old");
continue;
}
// after — configurable grace period via LEAN_* env (exposed in config/.env.sample)
$gracePeriod = (int) (config('lean.file_cleanup_grace_seconds') ?: 86400);
if ($fileAge < $gracePeriod) {
$this->warn("Skipping {$file} - file is less than " . intdiv($gracePeriod, 3600) . " hours old");
continue;
} Defensive patterns
Strategy: validation
Validate before calling
// Before running the cleanup for real, validate which files are actually eligible (>24h old)
use Illuminate\Support\Facades\Storage;
$storage = Storage::disk('local'); // same disk the command iterates
foreach ($storage->allFiles('uploads') as $file) {
$eligible = (time() - $storage->lastModified($file)) >= 86400;
if ($eligible) { /* will be considered for deletion on this run */ }
}
// And run the command's own dry-run first to preview behavior
// php bin/leantime files:cleanup-orphaned --dry-run Prevention
- Always do a --dry-run pass before the first real run on shared storage.
- Treat 'Skipping ... less than 24 hours old' as the designed grace period — schedule the command daily and let it converge.
- Keep server and storage-volume clocks NTP-synced so lastModified() never lies in the future.
- Do not lower the 86400-second threshold without reviewing what uploads link to entities asynchronously (queues, editor sessions).
When it happens
Trigger: Running the cleanup command (or its scheduled/cron invocation) shortly after users uploaded attachments, avatars, or file uploads from the Files domain/TinyMCE/Uppy flows; running it twice in a row on the same day (the second run re-encounters the same young files); running with --dry-run while validating which files would be removed; a file whose lastModified timestamp lies in the future due to clock skew on the storage volume (fileAge goes negative, still < 86400, so it is skipped).
Common situations: Operators seeing 'Skipping ...' lines in scheduled-task logs and mistaking them for failures; CI or Docker environments where storage/ is freshly seeded so every file appears young; testing cleanup behavior in a staging copy where all file mtimes were reset by the copy; expecting immediate reclamation of disk space after deleting tickets/comments.
Related errors
AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21).
Data as JSON: /api/errors/f804b81f4818e835.
Report an issue: GitHub.