{"record":{"id":"037dd818a6046805","repo":"getgrav/grav","slug":"decoding-csv-failed","errorCode":null,"errorMessage":"Decoding CSV failed","messagePattern":"Decoding CSV failed","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/File/Formatter/CsvFormatter.php","lineNumber":92,"sourceCode":"        foreach ($data as $row) {\n            $string .= $this->encodeLine($row, $delimiter);\n        }\n\n        return $string;\n    }\n\n    /**\n     * @param string $data\n     * @param string|null $delimiter\n     * @return array\n     * @see FileFormatterInterface::decode()\n     */\n    public function decode($data, $delimiter = null): array\n    {\n        $delimiter ??= $this->getDelimiter();\n        $lines = preg_split('/\\r\\n|\\r|\\n/', $data);\n        if ($lines === false) {\n            throw new RuntimeException('Decoding CSV failed');\n        }\n\n        // Get the field names\n        $headerStr = array_shift($lines);\n        if (!$headerStr) {\n            throw new RuntimeException('CSV header missing');\n        }\n\n        $header = str_getcsv($headerStr, $delimiter);\n\n        // Allow for replacing a null string with null/empty value\n        $null_replace = $this->getConfig('null');\n\n        // Get the data\n        $list = [];\n        $line = null;\n        try {\n            foreach ($lines as $line) {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/File/Formatter/CsvFormatter.php#L74-L110","documentation":"CsvFormatter::decode() splits the payload into lines with preg_split('/\\r\\n|\\r|\\n/', $data) and throws this only when preg_split itself fails (returns false) — a PCRE engine failure, not a CSV shape problem. The classic cause is exhausting pcre.backtrack_limit on very large input; malformed-but-splittable data produces the sibling 'CSV header missing' / 'Badly formatted CSV line' errors instead.","triggerScenarios":"Decoding a multi-megabyte CSV (or one enormous single line) through CsvFormatter when pcre.backtrack_limit (default ~1M) is exceeded; constrained runtimes where the PCRE engine errors during the split.","commonSituations":"Plugins importing large data exports via the formatter instead of a streaming reader; shared hosting with low PCRE limits; PHP-FPM pools with restrictive ini settings.","solutions":["Raise the limit for the import: ini_set('pcre.backtrack_limit', '10000000');.","Stream instead of loading whole: SplFileObject with READ_CSV flag or fgetcsv() row by row.","Chunk the input and decode pieces, preserving the header row for each chunk.","Diagnose with a manual split: preg_split(...) then preg_last_error() to confirm PREG_BACKTRACK_LIMIT_ERROR."],"exampleFix":"// before\n$rows = $csvFormatter->decode(file_get_contents('big.csv'));\n\n// after: stream rows, no whole-file regex split\n$file = new \\SplFileObject('big.csv');\n$file->setFlags(\\SplFileObject::READ_CSV | \\SplFileObject::SKIP_EMPTY);\n$rows = [];\nforeach ($file as $row) {\n    if ($row !== false && $row[0] !== null) { $rows[] = $row; }\n}","handlingStrategy":"try-catch","validationCode":"// Probe the engine before decode(): same split the formatter performs\n$lines = @preg_split('/\\r\\n|\\r|\\n/', $data);\nif ($lines === false) {\n    $err = preg_last_error(); // PREG_BACKTRACK_LIMIT_ERROR etc.\n    // chunk the data, raise pcre.backtrack_limit, or use a streaming reader\n}","typeGuard":null,"tryCatchPattern":"try {\n    $rows = $csvFormatter->decode($data);\n} catch (\\RuntimeException $e) {\n    // fall back to row-by-row streaming (SplFileObject::READ_CSV)\n    $rows = streamCsv($path);\n}","preventionTips":["Stream large CSV imports (fgetcsv/SplFileObject) instead of decoding whole files.","Raise pcre.backtrack_limit for import jobs that must load big payloads.","Cap accepted upload sizes to values the runtime can actually split."],"tags":["php","grav","csv","pcre","backtrack-limit","memory"],"backgroundTag":"regex-engine-error","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}