Grav Troubleshooting: Common Issues and Fixes | OpsBlu Docs

Grav Troubleshooting: Common Issues and Fixes

Common issues and solutions for Grav CMS sites including caching, plugin conflicts, and tracking problems.

This guide covers common issues specific to Grav CMS sites. Issues often relate to caching, Twig template rendering, and plugin interactions.

Common Issues Overview

Grav is a fast, simple, and flexible file-based CMS. Common troubleshooting scenarios involve cache management, Twig template debugging, plugin conflicts, and ensuring analytics work correctly with Grav's caching system.

Common Issue Categories

Performance Issues

  • Cache clearing and analytics
  • Plugin performance impact
  • Large site performance
  • Image processing overhead

Tracking Issues

  • Twig variable rendering issues
  • Admin session exclusion
  • Cached page tracking
  • Plugin event conflicts

Installation Problems

Grav Installation Issues

Server Requirements Not Met

Symptoms:

  • White screen on installation
  • PHP errors
  • Missing extensions warnings

Check requirements:

# PHP version (needs 7.3.6+, recommend 8.1+)
php -v

# Required extensions
php -m | grep -E "(curl|ctype|dom|gd|json|mbstring|openssl|session|simplexml|xml|zip)"

Install missing extensions:

# Ubuntu/Debian
sudo apt-get install php-curl php-gd php-mbstring php-xml php-zip

# CentOS/RHEL
sudo yum install php-curl php-gd php-mbstring php-xml php-zip

Permissions Issues

Error: "Unable to write to cache folder"

Fix permissions:

# From Grav root directory
cd /path/to/grav

# Set correct permissions
find . -type f | xargs chmod 644
find ./bin -type f | xargs chmod 755
find . -type d | xargs chmod 755
find . -type d | xargs chmod +s

# Ensure cache and logs are writable
chmod -R 775 cache/ logs/ images/ assets/ tmp/

Plugin Installation

Plugin Not Activating

Install via admin:

  1. Admin → Plugins
  2. Click "+ Add"
  3. Search for plugin
  4. Click "Install"

Install via CLI:

# From Grav root
bin/gpm install plugin-name

# List available plugins
bin/gpm index

# Update plugins
bin/gpm update

Manual installation:

# Download plugin to user/plugins/
cd user/plugins
git clone https://github.com/getgrav/grav-plugin-name.git plugin-name

Clear cache after install:

bin/grav cache --clear

Analytics Plugin Setup

Google Analytics Plugin

Install:

bin/gpm install google-analytics

Configure:

# user/config/plugins/google-analytics.yaml
enabled: true
tracking_id: 'G-XXXXXXXXXX'
position: 'head'  # or 'body'
objectType: 'gtag'  # or 'analytics'
forceSsl: true
anonymizeIp: true

Alternative - Manual Implementation:

{# user/themes/mytheme/templates/partials/analytics.html.twig #}
{% if not grav.user.username %}
    <!-- Google tag (gtag.js) -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'G-XXXXXXXXXX');
    </script>
{% endif %}

Include in base template:

{# user/themes/mytheme/templates/partials/base.html.twig #}
<head>
    {% block head %}
        {% include 'partials/analytics.html.twig' %}
    {% endblock %}
</head>

Configuration Issues

Issue Symptoms Common Causes Solutions
Cache Not Clearing Changes not appearing Cache persists Run bin/grav cache --clear or disable cache in dev
Admin Tracking Own visits in analytics No admin exclusion Add {\% if not grav.user.username \%} check
Twig Variables Missing Template errors Variable not defined Use {\{ variable|default('') \}\}
Plugin Conflicts Site breaks after plugin install Plugin incompatibility Disable plugins one by one to isolate
Markdown Not Rendering Raw markdown showing Markdown plugin disabled Enable Markdown plugin
Images Not Loading Broken image links Wrong path or cache Check paths and clear image cache
Form Not Submitting Form errors Form plugin misconfigured Check form plugin config and CSRF
Multi-language Issues Wrong language showing Language config incorrect Verify language plugin settings
Sitemap Not Updating Old sitemap Cache not cleared Clear cache and rebuild sitemap
Cached Pages Stale Old content showing Cache timeout too long Reduce cache lifetime or clear cache

Debugging with Developer Tools

Grav Debug Mode

Enable Debug Bar

Activate debugger:

# user/config/system.yaml
debugger:
  enabled: true
  provider: clockwork  # or debugbar
  censored: false

Debug bar shows:

  • PHP errors and warnings
  • Database queries (if using DB)
  • Twig rendering time
  • Memory usage
  • Loaded plugins
  • Cache statistics

Twig Debug Mode

Enable Twig debugging:

# user/config/system.yaml
twig:
  cache: false  # Disable cache during development
  debug: true   # Enable debug mode
  auto_reload: true  # Auto-reload templates
  autoescape: false

Debug in templates:

{# Dump all available variables #}
{{ dump() }}

{# Dump specific variable #}
{{ dump(page) }}
{{ dump(page.header) }}
{{ dump(config) }}

{# Check if variable exists #}
{% if page.header.custom_field is defined %}
    {{ page.header.custom_field }}
{% endif %}

{# Output variable type #}
Type: {{ attribute(page, 'header')|type }}

CLI Debugging Tools

Cache Management

Clear all cache:

# Clear everything
bin/grav cache --clear

# Clear specific cache types
bin/grav cache --clear-images
bin/grav cache --clear-cache
bin/grav cache --clear-compiled

# Purge cache (more aggressive)
bin/grav cache --purge

Check cache info:

# View cache statistics
bin/grav cache --stats

Check Configuration

List all settings:

# Show all system config
bin/plugin dev dump-system

Check plugin status:

# List installed plugins
bin/gpm list

# Check for updates
bin/gpm update

Log File Analysis

Check error logs:

# View recent errors
tail -f logs/grav.log

# Search for specific errors
grep "ERROR" logs/grav.log

# Check PHP errors
tail -f /var/log/php_errors.log

Enable detailed logging:

# user/config/system.yaml
errors:
  display: 1
  log: true

Platform-Specific Challenges

Caching System

Analytics Not Updating with Cache

Problem: Cached pages serve old analytics code

Solution 1 - Exclude analytics from cache:

# user/config/system.yaml
cache:
  enabled: true
  check:
    method: file
  driver: auto
  prefix: 'g'
  lifetime: 604800  # 7 days
  gzip: false

# Don't cache pages with dynamic analytics
pages:
  never_cache_twig: true  # For pages with dynamic content

Solution 2 - Use Twig's nocache:

{# Don't cache this block #}
{% cache 0 %}
    <!-- Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'G-XXXXXXXXXX');
    </script>
{% endcache %}

Solution 3 - Disable cache for specific pages:

# In page frontmatter (user/pages/01.home/default.md)
---
title: Home
cache_enable: false
---
Page content here

Admin Session Exclusion

Don't track logged-in users:

{% if not grav.user.username %}
    {# Analytics code only for non-admin users #}
    {% include 'partials/analytics.html.twig' %}
{% endif %}

More robust check:

{% set user = grav.user %}
{% if not user.authenticated %}
    {# Analytics for guests only #}
{% endif %}

Twig Template Rendering

Tracking Page Views

Get page metadata for analytics:

<script>
  // Track page with metadata
  gtag('event', 'page_view', {
    'page_title': '{{ page.title|e('js') }}',
    'page_location': '{{ page.url(true)|e('js') }}',
    'page_path': '{{ page.route|e('js') }}',
    'content_group': '{{ page.taxonomy.category[0]|default('')|e('js') }}'
  });
</script>

Track custom events:

{# Track downloads #}
{% for file in page.media.files %}
    <a href="{{ file.url }}" 'file_download', {
           'file_name': '{{ file.filename|e('js') }}',
           'file_extension': '{{ file.extension|e('js') }}'
       });">
        Download {{ file.filename }}
    </a>
{% endfor %}

Dynamic Content Tracking

Track taxonomy-based content:

{% set category = page.taxonomy.category[0]|default('uncategorized') %}
{% set tags = page.taxonomy.tag|join(', ')|default('none') %}

<script>
  gtag('config', 'G-XXXXXXXXXX', {
    'custom_map': {
      'dimension1': 'content_category',
      'dimension2': 'content_tags'
    }
  });

  gtag('event', 'page_view', {
    'content_category': '{{ category|e('js') }}',
    'content_tags': '{{ tags|e('js') }}'
  });
</script>

Plugin Event System

Tracking with Plugin Events

Create custom plugin:

# Create plugin structure
mkdir -p user/plugins/analytics-tracker

Plugin file:

<?php
// user/plugins/analytics-tracker/analytics-tracker.php
namespace Grav\Plugin;

use Grav\Common\Plugin;

class AnalyticsTrackerPlugin extends Plugin
{
    public static function getSubscribedEvents()
    {
        return [
            'onPluginsInitialized' => ['onPluginsInitialized', 0]
        ];
    }

    public function onPluginsInitialized()
    {
        if ($this->isAdmin()) {
            return;
        }

        $this->enable([
            'onPageContentRaw' => ['onPageContentRaw', 0],
            'onOutputGenerated' => ['onOutputGenerated', 0]
        ]);
    }

    public function onOutputGenerated()
    {
        // Don't track admin users
        if ($this->grav['user']->authenticated) {
            return;
        }

        // Inject analytics only for non-cached output
        $trackingCode = $this->getTrackingCode();
        $output = $this->grav->output;

        // Insert before </head>
        $output = str_replace('</head>', $trackingCode . '</head>', $output);
        $this->grav->output = $output;
    }

    private function getTrackingCode()
    {
        $config = $this->config->get('plugins.analytics-tracker');
        $trackingId = $config['tracking_id'] ?? '';

        if (empty($trackingId)) {
            return '';
        }

        return <<<HTML
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={$trackingId}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', '{$trackingId}');
</script>
HTML;
    }
}

Plugin config:

# user/plugins/analytics-tracker/analytics-tracker.yaml
enabled: true
tracking_id: 'G-XXXXXXXXXX'

Multi-Language Sites

Tracking Different Languages

Language-specific analytics:

{% set language = grav.language.getActive() %}

<script>
  gtag('config', 'G-XXXXXXXXXX', {
    'language': '{{ language }}',
    'content_group': 'Lang: {{ language }}'
  });
</script>

Different tracking IDs per language:

# user/config/site.yaml
google_analytics:
  en: 'G-AAAAAAAAAA'
  es: 'G-BBBBBBBBBB'
  fr: 'G-CCCCCCCCCC'
{% set language = grav.language.getActive() %}
{% set tracking_id = config.site.google_analytics[language]|default('') %}

{% if tracking_id %}
    <script async src="https://www.googletagmanager.com/gtag/js?id={{ tracking_id }}"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', '{{ tracking_id }}');
    </script>
{% endif %}

Error Messages and Solutions

Common Grav Errors

"Unable to locate template"

Error:

Twig\Error\LoaderError: Unable to locate template file: partials/base.html.twig

Check template paths:

# Template search order:
# 1. user/themes/mytheme/templates/
# 2. user/themes/mytheme/templates/partials/
# 3. system/templates/

# Verify file exists
ls -la user/themes/*/templates/partials/base.html.twig

"Undefined variable"

Error in Twig:

Twig\Error\RuntimeError: Variable "custom_field" does not exist

Safe access:

{# Wrong - will error if undefined #}
{{ page.header.custom_field }}

{# Correct - with default #}
{{ page.header.custom_field|default('') }}

{# Or check existence #}
{% if page.header.custom_field is defined %}
    {{ page.header.custom_field }}
{% endif %}

"Cache directory not writable"

Error:

RuntimeException: Cache directory /path/to/grav/cache is not writable

Fix:

# Set proper permissions
chmod -R 775 cache/
chown -R www-data:www-data cache/

# Or for development
chmod -R 777 cache/

Plugin Conflicts

Identify conflicting plugins:

# Disable all plugins
bin/gpm disable-all-plugins

# Enable one by one
cd user/plugins
for plugin in */; do
    echo "Testing: $plugin"
    # Enable plugin and test
done

Performance Problems

Slow Page Load

Enable performance optimizations:

# user/config/system.yaml
cache:
  enabled: true
  check:
    method: file
  driver: auto
  prefix: 'g'
  lifetime: 604800
  gzip: true  # Enable gzip

assets:
  css_pipeline: true
  css_minify: true
  js_pipeline: true
  js_minify: true

pages:
  markdown:
    extra: true
  process:
    markdown: true
    twig: false  # Disable Twig processing if not needed

Optimize images:

# Install image optimization plugin
bin/gpm install image-optimize

# Or manually optimize
find user/pages -name "*.jpg" -exec jpegoptim --max=85 {} \;
find user/pages -name "*.png" -exec optipng -o5 {} \;

Large Site Performance

Pagination for collections:

# In page header
content:
  items: '@self.children'
  order:
    by: date
    dir: desc
  limit: 10  # Items per page
  pagination: true

Lazy load images:

{% for image in page.media.images %}
    <img src="{{ image.url }}"
         loading="lazy"
         alt="{{ image.meta.alt|default('') }}">
{% endfor %}

When to Contact Support

Grav Community Support

Contact when:

  • Bug in Grav core
  • Plugin issues
  • Theme development questions
  • General configuration help

Support channels:

When to Hire a Developer

Complex scenarios:

  1. Custom plugin development
  2. Complex theme customization
  3. Performance optimization for large sites
  4. Custom analytics integration
  5. Multi-language site architecture
  6. E-commerce integration
  7. Custom API development
  8. Migration from other platforms

Advanced Troubleshooting

Complete Debug Setup

Development environment:

# user/config/system.yaml
debugger:
  enabled: true
  provider: debugbar
  censored: false

errors:
  display: 1
  log: true

twig:
  cache: false
  debug: true
  auto_reload: true

cache:
  enabled: false  # Disable during development

Performance Profiling

Enable profiling:

# Install Xdebug
sudo apt-get install php-xdebug

# Configure
# php.ini:
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug

Analyze with:

  • Webgrind
  • KCacheGrind
  • Blackfire.io

For platform-agnostic troubleshooting, see: