diff --git a/Hledger/Read/CsvReader.hs b/Hledger/Read/CsvReader.hs
--- a/Hledger/Read/CsvReader.hs
+++ b/Hledger/Read/CsvReader.hs
@@ -83,17 +83,9 @@
  handle (\e -> return $ Left $ show (e :: IOException)) $ do
   let throwerr = throw.userError
 
-  -- parse csv
-  records <- (either throwerr id . validateCsv) `fmap` parseCsv csvfile csvdata
-  return $ dbg "" $ take 3 records
-
-  -- identify header lines
-  -- let (headerlines, datalines) = identifyHeaderLines records
-  --     mfieldnames = lastMay headerlines
-
   -- parse rules
   let rulesfile = fromMaybe (rulesFileFor csvfile) mrulesfile
-  created <- records `seq` ensureRulesFileExists rulesfile
+  created <- ensureRulesFileExists rulesfile
   if created
    then hPrintf stderr "creating default conversion rules file %s, edit this file for better results\n" rulesfile
    else hPrintf stderr "using conversion rules file %s\n" rulesfile
@@ -101,14 +93,21 @@
   return $ dbg "" rules
 
   -- apply skip directive
-  let headerlines = maybe 0 oneorerror $ getDirective "skip" rules
+  let skip = maybe 0 oneorerror $ getDirective "skip" rules
         where
           oneorerror "" = 1
           oneorerror s  = readDef (throwerr $ "could not parse skip value: " ++ show s) s
-      records' = drop headerlines records
 
+  -- parse csv
+  records <- (either throwerr id . validateCsv skip) `fmap` parseCsv csvfile csvdata
+  dbgAtM 1 "" $ take 3 records
+
+  -- identify header lines
+  -- let (headerlines, datalines) = identifyHeaderLines records
+  --     mfieldnames = lastMay headerlines
+
   -- convert to transactions and return as a journal
-  let txns = map (transactionFromCsvRecord rules) records'
+  let txns = map (transactionFromCsvRecord rules) records
   return $ Right nulljournal{jtxns=sortBy (comparing tdate) txns}
 
 parseCsv :: FilePath -> String -> IO (Either ParseError CSV)
@@ -118,9 +117,9 @@
     _   -> return $ parseCSV path csvdata
 
 -- | Return the cleaned up and validated CSV data, or an error.
-validateCsv :: Either ParseError CSV -> Either String [CsvRecord]
-validateCsv (Left e) = Left $ show e
-validateCsv (Right rs) = validate $ filternulls rs
+validateCsv :: Int -> Either ParseError CSV -> Either String [CsvRecord]
+validateCsv _ (Left e) = Left $ show e
+validateCsv numhdrlines (Right rs) = validate $ drop numhdrlines $ filternulls rs
   where
     filternulls = filter (/=[""])
     validate [] = Left "no CSV records found"
@@ -409,8 +408,8 @@
   string "fields"
   optional $ char ':'
   many1 spacenonewline
-  f <- fieldname
   let separator = many spacenonewline >> char ',' >> many spacenonewline
+  f <- fromMaybe "" <$> optionMaybe fieldname
   fs <- many1 $ (separator >> fromMaybe "" <$> optionMaybe fieldname)
   restofline
   return $ map (map toLower) $ f:fs
diff --git a/Hledger/Reports/PostingsReport.hs b/Hledger/Reports/PostingsReport.hs
--- a/Hledger/Reports/PostingsReport.hs
+++ b/Hledger/Reports/PostingsReport.hs
@@ -66,7 +66,7 @@
           dbg "ps3" $ (if related_ opts then concatMap relatedPostings else id) $ -- with -r, replace each with its sibling postings
           dbg "ps2" $ filter (reportq `matchesPosting`) $                         -- filter postings by the query, including before the report start date, ignoring depth
           dbg "ps1" $ journalPostings $ journalSelectingAmountFromOpts opts j
-      (precedingps, reportps) = dbg "precedingps, reportps" $ span (beforestartq `matchesPosting`) pstoend
+      (precedingps, reportps) = dbg "precedingps, reportps" $ partition (beforestartq `matchesPosting`) pstoend
 
       empty = queryEmpty q
       -- displayexpr = display_ opts  -- XXX
diff --git a/hledger-lib.cabal b/hledger-lib.cabal
--- a/hledger-lib.cabal
+++ b/hledger-lib.cabal
@@ -1,5 +1,5 @@
 name:           hledger-lib
-version:        0.23
+version: 0.23.1
 stability:      stable
 category:       Finance, Console
 synopsis:       Core data types, parsers and utilities for the hledger accounting tool.
