Here's some useful things:
def sysValue(prop: String) = Option(System.getProperty(prop)) //returns Option[String]
def trySysValue(prop: String) = //returns Either[String, String]
  sysValue(prop) map Right getOrElse Left("Absent property: " + prop)
Then you can use monadic composition of Either through its right-projection
val batch = //batch is Either[String, (File, File)]
  for {
    x  <- trySysValue("XDir")).right
    xf <- dir(x).right
    y  <- trySysValue("YDir").right
    yf <- dir(y).right
  } 
  yield (xf, yf)
Where:
def dir(s: String) = { //returns Either[String, File]
  val f = new File(s)
  if (!f.exists()) Left("Does not exist: " + f)
  else if (!f.isDir()) Left("Is not a directory: " + f)
  else Right(f)
}
The left-hand-side of the Either will be an error message. This monadic composition is fail fast. You can achieve composition which will accumulate all failures (for example, if neither XDir nor YDir exist, you would see both messages) using scalaz Validation. In that case, the code would look like this:
def trySysValue(prop: String) = //returns Validation[String, String]
  sysValue(prop) map Success getOrElse ("Absent property: " + prop).fail
def dir(s: String) = {
  val f = new File(s)
  if (!f.exists())("Does not exist: " + f).fail
  else if (!f.isDir()) ("Is not a directory: " + f).fail
  else f.success
}
val batch = //batch is ValidationNEL[String, (File, File)]
  (trySysValue("XDir")) flatMap dir).liftFailNel <|*|> (trySysValue("YDir")) flatMap dir).liftFailNel