{"record":{"id":"ecd68f67867c6ee3","repo":"php-fig/log","slug":"psr-log-invalidargumentexception","errorCode":null,"errorMessage":"Psr\\Log\\InvalidArgumentException","messagePattern":"Psr\\\\Log\\\\InvalidArgumentException","errorType":"exception","errorClass":"Psr\\Log\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/InvalidArgumentException.php","lineNumber":5,"sourceCode":"<?php\n\nnamespace Psr\\Log;\n\nclass InvalidArgumentException extends \\InvalidArgumentException\n{\n}\n","sourceCodeStart":1,"sourceCodeEnd":8,"githubUrl":"https://github.com/php-fig/log/blob/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3/src/InvalidArgumentException.php#L1-L8","documentation":"Psr\\Log\\InvalidArgumentException is the PSR-3 logger-interface standard exception for invalid arguments passed to a PSR-3 logger. The class itself (src/InvalidArgumentException.php:5) is just an empty marker extending PHP's SPL \\InvalidArgumentException; psr/log never throws it directly. Instead, LoggerInterface::log() declares '@throws \\Psr\\Log\\InvalidArgumentException' (src/LoggerInterface.php:95), and concrete implementations (Monolog being the most common) throw it when the $level argument is not a valid PSR-3 / RFC 5424 level name: 'emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'.","triggerScenarios":"Calling $logger->log($level, $message) where $level is not one of the eight PSR-3 level strings: an unknown name like $logger->log('verbose', 'msg'), a wrong-case name like $logger->log('ERROR', 'msg') (levels are lowercase only), an integer severity (e.g. a syslog priority or old Monolog int constant), null, or an empty string. The level most often arrives dynamically from a config file, environment variable, database column, or message queue payload, so the invalid value only surfaces at runtime. The eight shorthand methods ($logger->error(), $logger->info(), ...) never hit this path because they hard-code a valid level.","commonSituations":"Config-driven log levels: a .env or YAML value like LOG_LEVEL=ERROR (uppercase) fed straight into log(). Monolog upgrades: Monolog 2 used integer level constants while Monolog 3 moved to a Level enum, so code passing numeric levels or calling Logger::addRecord with stale values breaks across major versions. Passing a custom/application-specific severity vocabulary ('notice', 'trace', 'fatal', 'verbose') that Monolog does not register. Case mismatch between LogLevel::ERROR ('error') and human-readable 'Error'/'ERROR' strings coming from logs APIs or user input.","solutions":["Switch to the dedicated shorthand methods or the Psr\\Log\\LogLevel constants so the level is always a known-valid literal: $logger->error($msg) or $logger->log(LogLevel::ERROR, $msg).","If the level comes from config/env, normalize and whitelist it before logging: lowercase it and check it against the eight LogLevel constants, substituting a safe default for unknown values.","If your app has its own severity vocabulary, map each internal level explicitly to a PSR-3 level with a lookup table instead of passing it through raw.","After a Monolog major-version upgrade (2 -> 3), audit every log()/addRecord() call site: numeric levels became Monolog\\Level\\* enum cases and unrecognized names now throw this exception.","Catch Psr\\Log\\InvalidArgumentException around log() only as a last-resort safety net so one bad level string cannot take down the request."],"exampleFix":"// before\n$level = getenv('LOG_LEVEL');          // e.g. 'ERROR' or 'verbose' -> throws\n$logger->log($level, 'Payment failed', ['id' => $paymentId]);\n\n// after\nuse Psr\\Log\\LogLevel;\n\n$validLevels = [\n    LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL,\n    LogLevel::ERROR, LogLevel::WARNING, LogLevel::NOTICE,\n    LogLevel::INFO, LogLevel::DEBUG,\n];\n$level = strtolower((string) getenv('LOG_LEVEL'));\nif (!in_array($level, $validLevels, true)) {\n    $level = LogLevel::INFO;            // safe default for bad config\n}\n$logger->log($level, 'Payment failed', ['id' => $paymentId]);","handlingStrategy":"validation","validationCode":"use Psr\\Log\\LogLevel;\n\nconst PSR3_LEVELS = [\n    LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL,\n    LogLevel::ERROR, LogLevel::WARNING, LogLevel::NOTICE,\n    LogLevel::INFO, LogLevel::DEBUG,\n];\n\nfunction normalizePsr3Level(mixed $level): string\n{\n    if (is_string($level)) {\n        $level = strtolower($level);\n        if (in_array($level, PSR3_LEVELS, true)) {\n            return $level;\n        }\n    }\n    return LogLevel::INFO; // safe default, never throws\n}\n\n// before calling log():\n$logger->log(normalizePsr3Level($configLevel), $message, $context);","typeGuard":"use Psr\\Log\\LogLevel;\n\nfunction isPsr3Level(mixed $level): bool\n{\n    return is_string($level)\n        && in_array($level, [\n            LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL,\n            LogLevel::ERROR, LogLevel::WARNING, LogLevel::NOTICE,\n            LogLevel::INFO, LogLevel::DEBUG,\n        ], true);\n}\n\n// usage:\nif (isPsr3Level($level)) {\n    $logger->log($level, $message, $context);\n} else {\n    $logger->info($message, $context + ['invalid_level' => $level]);\n}","tryCatchPattern":"use Psr\\Log\\InvalidArgumentException as LogInvalidArgumentException;\n\ntry {\n    $logger->log($level, $message, $context);\n} catch (LogInvalidArgumentException $e) {\n    // degrade gracefully: re-log at a guaranteed-valid level, keep the bad value in context\n    $logger->warning('Dropped log with invalid level {level}: {reason}', [\n        'level'  => is_scalar($level) ? (string) $level : get_debug_type($level),\n        'reason' => $e->getMessage(),\n        'exception' => $e,\n    ]);\n}","preventionTips":["Prefer the shorthand methods ($logger->error(), $logger->warning(), ...) over log(); they cannot receive an invalid level.","Always use the Psr\\Log\\LogLevel constants instead of hand-typed level strings; they eliminate case and spelling mistakes.","Never pass a raw config value, env var, DB field, or queue payload as $level without whitelist validation first.","Remember PSR-3 levels are lowercase only: 'ERROR' and 'Error' both throw; normalize with strtolower() before checking.","Add a startup/boot check that asserts every configured level constant is one of the eight valid names, failing fast with a clear config error.","When upgrading Monolog across major versions (2 -> 3), grep for ->log( and numeric level constants; the level representation changed and old values now throw."],"tags":["php","psr-3","logging","invalid-argument","log-level","monolog"],"backgroundTag":"invalid-log-level","analyzedSha":"f16e1d5863e37f8d8c2a01719f5b34baa2b714d3","analyzedAt":"2026-08-21T04:51:27.416Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}