serbanghita/Mobile-Detect · error · MobileDetectException

IS_TABLET_ERR

IS_TABLET_ERR

Error message

Cache problem in isTablet(): {$e->getMessage()}

What it means

isTablet() wraps its cache read/write in a try/catch and re-throws CacheException, CacheInvalidArgumentException, or a PSR InvalidArgumentException as MobileDetectException with code IS_TABLET_ERR (0x3), chaining the cause. The bundled in-memory Cache with the default 'sha1' cacheKeyFn cannot trigger it, so in practice a custom PSR-16 backend injected into MobileDetect threw, or createCacheKey() failed on a misconfigured cacheKeyFn. The IS_TABLET_ERR code plus the 'Cache problem in isTablet():' prefix tell you the detection logic itself is fine — the storage layer failed.

Source

Thrown at src/MobileDetect.php:1513

                //                        $result = $this->match($regexString, $this->getUserAgent());
                //                        if ($result) {
                //                            $this->cache->set($cacheKey, true, $this->config['cacheTtl']);
                //                            return true;
                //                        }
                //                    }
                //                } else {
                //                    // assume the regex is a "string"
                //                    if ($this->match($_regex, $this->getUserAgent())) {
                //                        $this->cache->set($cacheKey, true, $this->config['cacheTtl']);
                //                        return true;
                //                    }
                //                }
            }

            $this->cache->set($cacheKey, false, $this->config['cacheTtl']);
            return false;
        } catch (CacheInvalidArgumentException | CacheException | PsrInvalidArgumentException $e) {
            throw new MobileDetectException("Cache problem in isTablet(): {$e->getMessage()}", MobileDetectExceptionCode::IS_TABLET_ERR, $e);
        }
    }

    /**
     * Checks if a rule (e.g. isIphone, isIOS, etc.) matches its regex against the User-Agent.
     *
     * @param string $ruleName
     * @return bool
     * @throws MobileDetectException
     */
    public function is(string $ruleName): bool
    {
        if (!$this->hasUserAgent()) {
            throw new MobileDetectException('No user-agent has been set.', MobileDetectExceptionCode::INVALID_USER_AGENT_ERR);
        }

        if ($this->isUserAgentEmpty()) {
            return false;

View on GitHub (pinned to 6ab7b0404d)

Solutions

  1. Read $e->getPrevious() to find the underlying cache exception and fix the backend issue (connectivity, serializer, key rules).
  2. Ensure cacheKeyFn output is always a valid key: keep 'sha1' or use fn(string $k): string => hash('sha256', $k).
  3. Make the custom backend PSR-16-conformant: throw InvalidArgumentException only for invalid arguments, not for backend outages.
  4. Catch MobileDetectException narrowly, match IS_TABLET_ERR, and fall back to a default layout while logging the previous exception.

Example fix

// before
$layout = $detect->isTablet() ? 'tablet' : 'desktop';
// Redis down -> MobileDetectException: Cache problem in isTablet(): ...

// after
use Detection\Exception\MobileDetectException;
use Detection\Exception\MobileDetectExceptionCode;

try {
    $layout = $detect->isTablet() ? 'tablet' : 'desktop';
} catch (MobileDetectException $e) {
    if ($e->getCode() === MobileDetectExceptionCode::IS_TABLET_ERR) {
        $layout = 'desktop'; // cache degraded; log $e->getPrevious()
    } else {
        throw $e;
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight the backend you plan to inject:
$probeKey = sha1('probe');
try {
    $cache->set($probeKey, true, 1);
    $cache->get($probeKey);
} catch (\Throwable $e) {
    $cache = new \Detection\Cache\Cache(); // fall back to bundled in-memory cache
}
$detect = new MobileDetect();

Type guard

function isPsr16Backend(mixed $cache): bool
{
    return $cache instanceof \Psr\SimpleCache\CacheInterface;
}

Try / catch

catch (MobileDetectException $e) {
    if ($e->getCode() === MobileDetectExceptionCode::IS_TABLET_ERR) {
        $layout = 'desktop'; // cache degraded; keep serving
        error_log('detect cache: ' . $e->getPrevious()?->getMessage());
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: A Redis/APCu/filesystem PSR-16 adapter throwing inside get()/set() (connection down, serialization failure, its own key policy rejecting the key); 'cacheKeyFn' set to a non-callable or to a function returning keys violating the bundled charset rule; a DI container wiring a PSR-6 (not PSR-16) cache whose signatures don't match.

Common situations: Production-only backend failures that never show in dev because dev uses the default in-memory cache; swapping in a project-wide shared cache for cross-worker deduplication; Symfony/cache adapters with stricter key rules (spaces, braces, reserved characters) when cacheKeyFn was also customized.

Related errors


AI-assisted analysis of serbanghita/Mobile-Detect@6ab7b0404d (2026-08-21). Data as JSON: /api/errors/2232740e38ee4f5b. Report an issue: GitHub.