diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md
index 3b2e059c..fb3afe66 100644
--- a/docs/resources/changelog.md
+++ b/docs/resources/changelog.md
@@ -26,6 +26,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
 - Fixed a bug that prevented arrow keys from scrolling content within `<sl-dialog>` and `<sl-drawer>` [#925](https://github.com/shoelace-style/shoelace/issues/925)
 - Fixed a bug that prevented <kbd>Escape</kbd> from closing `<sl-dialog>` and `<sl-drawer>` in some cases
 - Fixed a bug that caused forms to submit unexpectedly when selecting certain characters [#988](https://github.com/shoelace-style/shoelace/pull/988)
+- Fixed a bug in `<sl-radio-group>` that prevented the `invalid` property from correctly reflecting validity sometimes [992](https://github.com/shoelace-style/shoelace/issues/992)
 - Improved `<sl-badge>` to improve padding and render relative to the current font size
 - Updated Lit to 2.4.1
 - Updated TypeScript to 4.8.4
diff --git a/src/components/radio-group/radio-group.test.ts b/src/components/radio-group/radio-group.test.ts
index 0317bc3d..5508f1ca 100644
--- a/src/components/radio-group/radio-group.test.ts
+++ b/src/components/radio-group/radio-group.test.ts
@@ -6,6 +6,31 @@ import type SlRadioGroup from './radio-group';
 
 describe('<sl-radio-group>', () => {
   describe('validation tests', () => {
+    it('should be invalid initially when required and no radio is checked', async () => {
+      const radioGroup = await fixture<SlRadioGroup>(html`
+        <sl-radio-group required>
+          <sl-radio value="1"></sl-radio>
+          <sl-radio value="2"></sl-radio>
+        </sl-radio-group>
+      `);
+
+      expect(radioGroup.invalid).to.be.true;
+    });
+
+    it('should become valid when an option is checked', async () => {
+      const radioGroup = await fixture<SlRadioGroup>(html`
+        <sl-radio-group required>
+          <sl-radio value="1"></sl-radio>
+          <sl-radio value="2"></sl-radio>
+        </sl-radio-group>
+      `);
+
+      radioGroup.value = '1';
+      await radioGroup.updateComplete;
+
+      expect(radioGroup.invalid).to.be.false;
+    });
+
     it(`should be valid when required and one radio is checked`, async () => {
       const el = await fixture<SlRadioGroup>(html`
         <sl-radio-group label="Select an option" value="1" required>
diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts
index 137cd70d..7b50ddb4 100644
--- a/src/components/radio-group/radio-group.ts
+++ b/src/components/radio-group/radio-group.ts
@@ -85,6 +85,10 @@ export default class SlRadioGroup extends ShoelaceElement {
     this.defaultValue = this.value;
   }
 
+  firstUpdated() {
+    this.invalid = !this.validity.valid;
+  }
+
   /** Sets a custom validation message. If `message` is not empty, the field will be considered invalid. */
   setCustomValidity(message = '') {
     this.customErrorMessage = message;
@@ -229,6 +233,7 @@ export default class SlRadioGroup extends ShoelaceElement {
   updateCheckedRadio() {
     const radios = this.getAllRadios();
     radios.forEach(radio => (radio.checked = radio.value === this.value));
+    this.invalid = !this.validity.valid;
   }
 
   render() {