How to specify vertex attributes in client-side vertex array?

I'm porting OpenGL 3.X code to OpenGL 2.1, where no VAO available. The codes below make my program crash inside graphics driver, immediately at glDrawElements call.

Part of the code is shared between 3.X and 2.1 mode, and it works properly in OpenGL 3.X above.

struct StrokeVertex
{
    glm::vec2 position;
    glm::vec2 tangent;
    float center_dist;
};

if (use_gl3)
    bind_vao();

bind_vbo();
bind_ibo();
send_data();
bind_program();
set_uniforms();

// setup client-side vertex array here
if (!use_gl3)
{
    glEnableClientState( GL_VERTEX_ARRAY );
    GLHELPER_CHECK;
        
    glEnableVertexAttribArray( 0 );
    GLHELPER_CHECK;
    glVertexAttribPointer( get_attribute_location( "tangent" ) /* got 1 here */, 2, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, tangent ) );
    GLHELPER_CHECK;
        
    glEnableVertexAttribArray( 1 );
    GLHELPER_CHECK;
    glVertexAttribPointer( get_attribute_location( "center_dist" ) /* got 2 here */, 1, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, center_dist ) );
    GLHELPER_CHECK;

    // I also tried call this before setting the two attributes
    glVertexPointer( 2, GL_FLOAT, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, position ) );
    GLHELPER_CHECK;
}

// immediately crash here
glDrawElements( GL_TRIANGLES, GLsizei( n_elem ), GL_UNSIGNED_INT, nullptr );
GLHELPER_CHECK;

if (!use_gl3)
{
    glDisableClientState( GL_VERTEX_ARRAY );
}
else
{
    unbind_vao();
}

And the part of vertex shader of OpenGL 3.X and 2.X. Most part are same expect attribute declaration:

in vec2 logic_pos;
in vec2 tangent;
in float center_dist;

OpenGL 2.X:

// builtin gl_Vertex is used, so we omit logic_pos
attribute vec2 tangent;
attribute float center_dist;

There seems to be a mismatch regarding which vertex attributes get enabled and to which data gets bound:

The following code tells OpenGL that it should enable vertex attribute 0, but the data gets bound to attribute 1 (at least according to the comment).

glEnableVertexAttribArray( 0 );
glVertexAttribPointer( get_attribute_location( "tangent" ) /* got 1 here */, 2, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, tangent ) );

In total, it looks as if your code enables attributes 0 and 1, but binds data to 1 and 2.

If you have attributes enabled but do not bind any data to them, this might lead to the crash you describe.