{"record":{"id":"b0ff60f5a73e415c","repo":"getgrav/grav","slug":"csv-header-missing","errorCode":null,"errorMessage":"CSV header missing","messagePattern":"CSV header missing","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/File/Formatter/CsvFormatter.php","lineNumber":98,"sourceCode":"\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) {\n                if (!empty($line)) {\n                    $csv_line = str_getcsv($line, $delimiter);\n\n                    if ($null_replace) {\n                        array_walk($csv_line, static function (&$el) use ($null_replace) {\n                            $el = str_replace($null_replace, \"\\0\", $el);","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/File/Formatter/CsvFormatter.php#L80-L116","documentation":"CsvFormatter::decode() treats the first line as a mandatory header row: after splitting, array_shift removes it and throws when it is falsy. Concretely the payload's first line is empty — the data is an empty string, contains only whitespace/newlines, or begins with blank line(s) — so there are no field names to map columns against.","triggerScenarios":"decode(''), decode(\"\\n\\n1,2\"); a CSV export written without its header row; a file truncated so the first line is blank; uploads that are empty before validation.","commonSituations":"Export tools configured to omit headers; plugins feeding user uploads directly into the formatter; empty seed files shipped with a plugin; files saved with a stray leading newline.","solutions":["Guard empty input before decoding: if (trim($data) === '') treat as empty dataset ([] or defaults), not an error.","Fix the source to emit a header row (re-export with headers enabled).","Strip leading blank lines when they are expected: $data = ltrim($data, \"\\r\\n\").","If the schema is known, prepend the header yourself: $data = implode(',', $fields) . \"\\n\" . $data;"],"exampleFix":"// before\n$list = $formatter->decode($raw);\n\n// after\n$raw = is_string($raw) ? ltrim($raw, \"\\r\\n\") : '';\n$list = ($raw === '') ? [] : $formatter->decode($raw);","handlingStrategy":"validation","validationCode":"if (!is_string($data) || trim($data) === '') {\n    // no header possible: treat as empty dataset instead of calling decode()\n    return [];\n}","typeGuard":null,"tryCatchPattern":"try {\n    $list = $formatter->decode($data);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'CSV header missing')) {\n        $list = []; // empty input is not an error for the caller\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Validate uploads/imports are non-empty before parsing.","Ensure export tools emit a header row.","Strip leading blank lines from inbound CSV at the boundary."],"tags":["php","grav","csv","header","empty-input","parse-error"],"backgroundTag":"file-parse-failed","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}