Files
intotheeast-com/docs/solutions/integration-issues/grav-plugin-config-without-code-wont-enable.md
T
m038andClaude Fable 5 24867524a1 docs(solutions): compound refresh — fix reference drift, grow CONCEPTS.md
Refresh audit of all 13 docs/solutions learnings against the current
codebase. Core guidance verified accurate everywhere; three docs had
reference drift:

- dual-repo-submodule-workflow: point worktree setup/teardown at the
  make worktree-new/worktree-rm targets (manual procedure misses
  .worktree-env isolation)
- docker-exec-root-owned-bind-mount-files: tracked-plugin list now
  includes entry-actions; fix-perms description matches actual target
- grav-plugin-config-without-code-wont-enable: 3-category model's
  custom-in-repo list now includes entry-actions

CONCEPTS.md: add Container, Content repo, Outer repo, Pin, Env tree,
Remote-only plugin; refresh Active Trip (switching is one setting now).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0195b3cDdMeize2Mm1FgC2aU
2026-07-08 23:37:56 +02:00

8.0 KiB

title, date, category, module, problem_type, component, severity, symptoms, root_cause, resolution_type, related_components, tags
title date category module problem_type component severity symptoms root_cause resolution_type related_components tags
Grav plugin won't enable because its code is missing while its config persists in the env tree 2026-07-05 integration-issues git-sync integration_issue tooling high
git-sync plugin will not enable on prod despite enabled: true in its config
Plugin does not appear / cannot be toggled on in the Grav Admin UI
Config-level fix attempts (editing plugin YAML) have no effect
make remote-gpm-install-prod PKG=git-sync reports a FRESH install, not 'already installed'
incomplete_setup dependency_update
documentation
development_workflow
grav
git-sync
gpm
plugin-management
env-config
config-without-code
troubleshooting-order
remote-only-plugin

Grav plugin won't enable because its code is missing while its config persists in the env tree

Problem

After remediating an unrelated git-sync secret leak on prod (intotheeast.com, Grav 2.0.7 on a DirectAdmin/Apache shared host), the operator went to re-enable the git-sync plugin. Setting enabled: true in its config had no effect: the plugin would not appear as enabled in the Grav Admin plugins UI, and toggling it on manually in the browser did not take either. It looked fully "configured" — the config file was right there — but the plugin was inert and sync never ran.

The trap is that the plugin's config file existed (in the per-host env tree at user/env/intotheeast.com/config/plugins/git-sync.yaml), so any inspection that reads only config concluded the plugin was present and just needed enabling. The actual problem was one layer down: the plugin's code was missing from user/plugins/git-sync/. A Grav plugin cannot load or enable without its code on disk, no matter what its config says.

Symptoms

  • The git-sync plugin does not appear as enabled, and cannot be enabled, in the Grav Admin UI — despite enabled: true being present in its config.
  • Toggling enabled in config, or flipping the toggle in the Admin UI, produces no working plugin. Sync does not run.
  • The plugin's config file DOES exist (in the per-environment tree user/env/intotheeast.com/config/plugins/git-sync.yaml), so the plugin appears "present" whenever only the config is inspected — masking the real state.

What Didn't Work

  1. Setting / ensuring enabled: true in the git-sync config. No effect. Config was never the problem.
  2. Enabling the plugin manually in the Admin UI. The toggle wouldn't take.

Both failed attempts operate on the config layer. But the plugin's code was absent from user/plugins/git-sync/, and Grav can't load a plugin without its code. Grav (and any tooling that reads the config tree) reports a plugin as "configured" purely from the presence of its config file, which masks the absence of code. Diagnosing and poking at the config layer could never fix a missing-code problem — and guessing at config changes before running a simple ls on the plugin directory cost real time here.

Solution

First, run the decisive diagnostic on the server — confirm whether the plugin code actually exists before touching config:

ls -la $WEBROOT/user/plugins/git-sync/    # empty/absent  => missing code, reinstall

(In this project, do that via a make target or an ssh one-liner the user runs — never raw SSH by the assistant. All server ops go through make remote-*.)

With the directory confirmed empty/absent, reinstall the plugin's code via GPM:

make remote-gpm-install-prod PKG=git-sync   # GPM fresh-installs Git Sync v3.4.4

That make target runs, on the server:

php bin/gpm index -f && php bin/gpm install git-sync -y && php bin/grav clearcache

The install output read "Preparing to install Git Sync [v3.4.4] ... Success!" — a fresh install, not "already installed." That fresh-install line is exactly what confirmed the code had been absent all along. After the reinstall plus cache clear, the plugin enabled and sync worked.

Why This Works

Grav resolves a plugin from two independent locations:

  • Code at user/plugins/<name>/ — installed by GPM. Note user/plugins/ is gitignored (/plugins/*) and is NOT tracked by the content repo.
  • Config — the tracked user/config/plugins/<name>.yaml and/or the per-host user/env/<host>/config/plugins/<name>.yaml.

These two can desync: config can exist with no code behind it. Config alone makes the plugin look present to any tool that only reads config, but the plugin stays inert until its code is on disk. GPM reinstall restores the code; clearcache makes Grav re-scan and pick it up.

A project-specific amplifier made this worse: git-sync is a remote-only, GPM-managed plugin. It is deliberately NOT in plugins.txt, so make install-plugins and the normal make remote-install flow do not restore it. Only an explicit php bin/gpm install git-sync (via make remote-gpm-install-prod PKG=git-sync) does. So when its code goes missing, it does not self-heal through the standard install path — you must reinstall it explicitly.

How the code went missing here is unconfirmed. It happened around the git-sync secret-leak remediation, but the exact step that wiped user/plugins/git-sync/ was not established — don't assume a specific cause.

Prevention

  • Check the code layer before the config layer. When a Grav plugin "won't enable" and config toggles do nothing, FIRST verify the code exists: ls user/plugins/<name>/ on the server. Config-without-code is the failure class; the empty directory is the tell.
  • Enumerate both layers in all locations when diagnosing. Plugins have a code layer (user/plugins/<name>/) and a config layer, and on prod the config can live in the env tree (user/env/<host>/config/plugins/<name>.yaml) and persist completely independently of the code. Remember: once user/env/<host>/ exists, Grav Admin writes ALL config there, so always check both user/config/... and the env path (env wins).
  • Know which plugins are remote-only. The 3-category model: GPM-via-plugins.txt (admin2 / api / flex-objects), custom-in-repo (cache-on-save / story-blocks / entry-actions), and remote-only (git-sync — never in plugins.txt). Remote-only plugins are NOT restored by the standard install/content flows, so reinstall them explicitly via GPM after any operation that could have wiped user/plugins/.
  • Diagnose actual state before proposing config fixes. An ls is cheaper than a guess. Establishing that the code was missing would have pointed straight at the reinstall instead of a round of config poking.
  • docs/solutions/integration-issues/stale-grav-version-blocks-api-plugin-install.mdclosest sibling. Same family (a plugin non-functional on prod, resolved by a GPM install + cache clear), same 2026-07-04/05 cutover context, same plugins.txt / make remote-* / GPM machinery. Distinct trigger: there, GPM refuses to offer the plugin because the installed core is below the version floor; here, the plugin's code folder is simply missing while its config persists (config-without-code desync). Two different ways a plugin ends up absent/inert on prod.
  • docs/solutions/architecture-patterns/git-sync-secret-exposure-and-tracked-file-boomerang.md — same module (git-sync) and explains why the config survived without code: Grav Admin writes git-sync.yaml into the per-environment tree user/env/<host>/config/plugins/, which is untracked/gitignored and not part of the plugin package. The orphaned config here is the flip side of that env-tree behavior.
  • docs/solutions/conventions/grav-plugin-config-must-be-tracked-override.md — establishes the plugin code (GPM/gitignored) vs config (tracked override / env tree) split that this bug exploits. This doc is a concrete failure of that split going the other way: config present (in the env tree), code absent.
  • docs/working/git-sync-notes.md — operational notes on git-sync's per-environment tree, where git-sync.yaml lives server-only. Context for where the orphaned config resided.