Based on the OP's dataset, it seems like a data.table.  For subsetting columns in data.table, we need with = FALSE
df[, cnm, with = FALSE]
#   X2 X3
#1:  1  3
#2:  5  3
#3:  3  4
#4:  6  5
#5:  2  2
According to the ?data.table documentation
with - By default with=TRUE and j is evaluated within the frame of x;
  column names can be used as variables. 
When with=FALSE j is a character vector of column names, a numeric
  vector of column positions to select or of the form startcol:endcol,
  and the value returned is always a data.table. with=FALSE is often
  useful in data.table to select columns dynamically. Note that x[,
  cols, with=FALSE] is equivalent to x[, .SD, .SDcols=cols].
If the dataset is data.frame, just 
setDF(df)#convert to 'data.frame'
df[cnm]
#   X2 X3
#1  1  3
#2  5  3
#3  3  4
#4  6  5
#5  2  2
will subset the dataset
The [[ is for extracting a single column of data.frame or list element
Applying the OP's code in a data.table gets the same error message
df[[cnm]]
Error in .subset2(x, i, exact = exact) : subscript out of bounds
If we do the data.frame subsetting option in data.table, it will not work either
df[cnm]
Error in [.data.table(df, cnm) :    When i is a data.table (or
  character vector), the columns to join by must be specified either
  using 'on=' argument (see ?data.table) or by keying x (i.e. sorted,
  and, marked as sorted, see ?setkey). Keyed joins might have further
  speed benefits on very large data due to x being sorted in RAM.