Query gives correct answer on second run
There's so much wrong with your query.

In your subquery sort you do a left join, but your where clause actually converts it to an inner join. 

You seem to rely on the order by in the sort subquery. You don't do an order by in the outer query. Maybe you can practically rely on it to work, but in theory you can not. 

Your variables @curType and @curRow are nowhere initialized. Either do this before the query with a SET @variable = 'whatever' command or in a cross joined subquery in your query. This is most likely the cause, why it gives a "correct answer" on the second run. Variables have session scope! Not query scope. You can not rely on them to be NULL or whatever you want them to be when you don't initialize them.

The keyword AND is not used to do two commands! It's used in boolean expressions, which return either 1 (true) or 0 (false) or NULL (unknown, if one of the operands is NULL. So, better check your result, if it really is correct ;)

Since it's not totally clear to me, what you're trying to do, I can only have a guess. But something like this may be what you're looking for. 

SELECT * FROM (
    SELECT a.*,
    @curRow := IF(concat(question_id,`type`) = @curType, @curRow + 1, 1) AS rank,
    @curType := concat(question_id,`type`)
    FROM
    ul_attempt_responses a
    INNER JOIN us_attempts b ON a.attempt_id = b.id
    JOIN (SELECT @curType := NULL, @curRow := 0) var_init_subquery
    WHERE b.user_id = 3
    AND response IS NOT NULL
    AND trim(response) != ''
    AND b.authenticated = 1
    ORDER BY question_id, type, id DESC
) final WHERE rank = 2;