Actually, it is not skipped but it causes the concatenation to fail silently.
X is NULL.
When you do '|| X ||', the NULL value of X is not treated as a string but as a missing value.
String concatenation with NULL ('|| NULL ||') results in the entire concatenation expression becoming NULL.
Thus, the v_sql assignment evaluates to NULL, and you see NVL(,NULL) in your result
If you want the literal text NULL to appear in the SQL query when X is NULL, you need to explicitly handle this substitution. For example:
v_sql := 'insert into users(id) values(NVL(' || CASE WHEN X IS NULL THEN 'NULL' ELSE X END || ', NULL))';