Custom theme options Radio inputs not saving

Why isn’t the following option getting saved?

Because the database option name (the second parameter for register_setting()) is aa_myoption, hence the input name has to be aa_myoption or in the array notation like aa_myoption[my_field]:

<input name="aa_myoption">           <!-- like this -->
<input name="aa_myoption[my_field]"> <!-- or this -->

Secondly, when you retrieve the option value and when it is an array of settings (like the second <input> above), and as with any other arrays, you should check if an array key exists before attempting to use the value:

<?php
$options = (array) get_option( 'aa_myoption', array() );
$my_field = isset( $options['my_field'] ) ? $options['my_field'] : '';

// Then for example when using checked():
?>
<input name="aa_myoption[my_field]" type="checkbox" value="myvalue"<?php checked( $my_field, 'myvalue' ); ?>>
<?php
// Or you can also use wp_parse_args() to make sure all KEYS are set:
$options = wp_parse_args( get_option( 'aa_myoption', array() ), array(
    'my_field' => '',
) );

// Then for example when using checked():
?>
<input name="aa_myoption[my_field]" type="checkbox" value="myvalue"<?php checked( $options['my_field'], 'myvalue' ); ?>>

Additional notes

  1. $my_field = isset( $options['my_field'] ) ? $options['my_field'] : '';

    The empty string doesn’t necessarily need to be empty, we’re simply assigning a default value to $my_field when the array key/item my_field doesn’t already exist, and you can even use the PHP 7 format — $my_field = $options['my_field'] ?? ''; which does the same thing as the one above. =)

  2. $options = (array) get_option( 'aa_myoption', array() );

    A bit of explanation:

    • I’m telling get_option() to return an empty array if the option does not exist yet, i.e. the database query WHERE option_name="aa_myoption" returned no results.

    • I’m type-casting the value to an array to make sure it’s indeed an array, because WordPress options (both custom and core options like admin_email) can be easily filtered by plugins and your own code.. e.g. via the option_<option name> hook, and it is possible that a plugin (or custom code) returned a non-array value.

      But of course, the best practice is for filter callbacks to always return the proper type of value.

      And I removed the sample test cases from this answer, but you can always check them here. 😉

      Also, I’d likely use the wp_parse_args(), but I’d probably create a my_theme_get_options() function or something like that, which returns an array with options from the database and merged with the default values. 🙂