DEV Community

Cover image for "Invalid OAuth2 Access Token" in CF7 Google Sheets Connector: What the Stack Trace Actually Tells You
Rahul Sharma
Rahul Sharma

Posted on

"Invalid OAuth2 Access Token" in CF7 Google Sheets Connector: What the Stack Trace Actually Tells You

A developer posted this error on the WordPress forums after connecting their CF7 form to Google Sheets:

[ERROR_MSG] => Auth, Invalid OAuth2 access token
[TRACE_STK] => #0 class-gs-service.php(182): CF7GSC_googlesheet->auth()
Enter fullscreen mode Exit fullscreen mode

The plugin author replied: probably a Google library conflict with another plugin. Deactivate everything else and test.

That is one of three possible causes. The stack trace tells you much more if you know how to read it. This post breaks down what is actually happening, why this error appears even when your connection was working fine, and how to fix each cause.


Reading the Stack Trace

The stack trace is not noise. It is a precise execution path. Here is what it tells you:

#0  class-gs-service.php(182): CF7GSC_googlesheet->auth()
#1  class-wp-hook.php(308):    Gs_Connector_Service->cf7_save_to_google_sheets()
#4  submission.php(119):       do_action('wpcf7_mail_sent', ...)
#10 rest-api.php(357):         WPCF7_ContactForm->submit()
Enter fullscreen mode Exit fullscreen mode

Reading bottom-up (which is how stack traces work):

  1. A user submits a CF7 form via the REST API (rest-api.php)
  2. CF7 processes the submission (submission.php)
  3. CF7 fires the wpcf7_mail_sent action after the email is sent (submission.php:119)
  4. GSheetConnector catches that hook and calls cf7_save_to_google_sheets()
  5. That function calls auth() to validate the Google OAuth token
  6. auth() fails with Invalid OAuth2 access token

The form submitted successfully. The email was sent. The Google Sheets write is what failed. This is important: the form is not broken, only the Sheets logging is broken.

The failure happens at class-gs-service.php(182) inside the auth() method. That method calls Google's OAuth token validation endpoint. The token it holds is being rejected by Google.

Three Causes of "Invalid OAuth2 Access Token"

Cause 1: Google OAuth Token Expired (Most Common)

Google OAuth access tokens issued through the standard authorization flow expire. When GSheetConnector connects to Google, it stores:

  • An access token (short-lived, expires in 1 hour)
  • A refresh token (long-lived, used to get new access tokens)

The plugin should automatically use the refresh token to get a new access token before each Sheets write. If the refresh token itself has expired or been revoked, no new access token can be generated and every write fails with this error.

When do refresh tokens expire or get revoked?

  • Google revokes refresh tokens after 7 days if the OAuth app is in "Testing" status in Google Cloud Console (not published/verified)
  • Refresh tokens are revoked when the user changes their Google account password
  • Refresh tokens are revoked when the user revokes app access in their Google Account > Security > Third-party apps
  • Google revokes tokens for apps that have been inactive for 6 months
  • Each user account is limited to 50 active refresh tokens per app. The 51st token revokes the oldest one.

The 7-day expiry is the most common cause for developers using GSheetConnector. If you connected successfully, it worked for a week, then broke, this is almost certainly what happened. Your app is in Testing status in Google Cloud Console.

Fix: Publish your OAuth app in Google Cloud Console (change from Testing to Production), then reconnect the plugin. Or reconnect every 7 days if you intentionally keep it in Testing.

Cause 2: Google Client Library Version Conflict

This is what the plugin author pointed to. The GSheetConnector plugin bundles a version of Google's PHP client library (google/apiclient). Other plugins that integrate with Google services (Google Analytics, Google Ads, Google Tag Manager plugins, WooCommerce Google integrations) may bundle a different version of the same library.

When two plugins load different versions of Google_Client, Google_Service_Sheets, or related classes, PHP class conflicts occur. Depending on which plugin loads first, the wrong version of the auth class runs. The token validation logic in the wrong version may reject tokens it should accept, or fail silently.

How to confirm:

Deactivate all other Google-related plugins one by one. If the error disappears after deactivating a specific plugin, that is the conflicting one.

You can also check for class conflicts:

// Add temporarily to functions.php
add_action('plugins_loaded', function() {
    if (class_exists('Google_Client')) {
        $reflection = new ReflectionClass('Google_Client');
        error_log('Google_Client loaded from: ' . $reflection->getFileName());
    }
});
Enter fullscreen mode Exit fullscreen mode

If the path shown does not point to GSheetConnector's directory, another plugin loaded its version first.

Fix: Use a plugin that handles Google library loading with proper namespacing or version isolation. Or use Contact Form to API to send CF7 data to Google Sheets via the Sheets API directly with a Bearer token, bypassing the shared library problem entirely.

Cause 3: Wrong Google Account or Revoked Access

If the Google account used to authorize GSheetConnector has had its access revoked (either manually by the user or automatically by Google), the stored tokens are invalid.

Common scenarios:

  • The Google account used for authorization belongs to a team member who left the organization and had their account suspended
  • The Google Workspace admin revoked third-party app access across the organization
  • The specific Google Sheet was moved to a different Drive account that the authorized user cannot access

Fix: Reconnect the plugin using the correct Google account that has access to the target spreadsheet.

Verifying the Token State Without the Plugin

You can test whether a stored token is valid directly:

# Test if an access token is valid
curl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=YOUR_ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

A valid token returns:

{
  "issued_to": "your-client-id.apps.googleusercontent.com",
  "audience": "your-client-id.apps.googleusercontent.com",
  "scope": "https://www.googleapis.com/auth/spreadsheets",
  "expires_in": 3412,
  "access_type": "online"
}
Enter fullscreen mode Exit fullscreen mode

An invalid or expired token returns:

{
  "error": "invalid_token",
  "error_description": "Token has been expired or revoked."
}
Enter fullscreen mode Exit fullscreen mode

If you get invalid_token here, the stored access token is genuinely expired. The question is whether the refresh token is also expired. If it is, reconnecting the plugin from scratch is the only fix.

Checking Google Cloud Console OAuth App Status

This is the step most developers miss. Go to:

  1. Google Cloud Console
  2. Select your project
  3. Go to APIs and Services > OAuth consent screen
  4. Check the Publishing status

If it says Testing, your refresh tokens expire after 7 days and only test users you have explicitly added can authorize the app. Change to Production (you do not need Google verification for internal tools used by your own account) to get non-expiring refresh tokens.

The Direct API Alternative

GSheetConnector's OAuth dependency is the source of all three failure modes above. The plugin needs a valid access token every time a form submits. If the token lifecycle breaks for any reason, all Sheets writes fail silently (or with this error logged to PHP error logs that most site owners never check).

An alternative is to connect CF7 directly to the Google Sheets API using a Service Account instead of OAuth user authorization. Service accounts use a JSON key file for authentication and do not have token expiry issues in the same way.

The Google Sheets API endpoint for appending a row:

POST https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/{range}:append
Authorization: Bearer SERVICE_ACCOUNT_ACCESS_TOKEN
Content-Type: application/json

{
  "values": [["John Smith", "john@example.com", "2024-01-15"]]
}
Enter fullscreen mode Exit fullscreen mode

Contact Form to API can send CF7 form data to this endpoint directly, with the service account token handled separately from the plugin's OAuth flow. This removes the shared Google library dependency and the 7-day token expiry problem.

Diagnosis Checklist

Work through these in order:

  1. Check Google Cloud Console OAuth consent screen — is the app in Testing status?
  2. Has it been more than 7 days since you last connected the plugin?
  3. Did the Google account owner change their password recently?
  4. Are there other Google-related plugins active? Deactivate them one by one.
  5. Does the authorized Google account still have access to the target spreadsheet?
  6. Test the access token directly with the tokeninfo endpoint above

Top comments (0)