The pre-flight live test checklist and fixes for join, speech, creation, and Twilio failures. Part of the Google Meet plugin guide.

Live test checklist

Before handing a meeting to an unattended agent:

apus googlemeet setup
apus nodes status
apus googlemeet test-speech https://meet.google.com/abc-defg-hij \
  --transport chrome-node \
  --message "Say exactly: Google Meet speech test complete."

Expected Chrome-node state:

For a remote Chrome host such as a Parallels macOS VM, the shortest safe check after updating the Gateway or the VM:

apus googlemeet setup
apus nodes status --connected
apus nodes invoke \
  --node parallels-macos \
  --command googlemeet.chrome \
  --params '{"action":"setup"}'

That proves the Gateway plugin is loaded, the VM node is connected with the current token, and the Meet audio bridge is available before an agent opens a real meeting tab.

For a Twilio smoke, use a meeting that exposes phone dial-in details:

apus googlemeet setup
apus googlemeet join https://meet.google.com/abc-defg-hij \
  --transport twilio \
  --dial-in-number +15551234567 \
  --pin 123456

Expected Twilio state:

Troubleshooting

Agent cannot see the Google Meet tool

Confirm the plugin is enabled and its runtime application succeeded; see Apply changes and inspect. Then check setup:

apus plugins list | grep google-meet
apus googlemeet setup

On Linux, local Chrome talk-back requires PipeWire-Pulse in the Chrome desktop user's session plus pactl, pacat, and parec. On unsupported operating systems, use mode: "transcribe", Twilio dial-in, or a supported macOS/Linux chrome-node host.

No connected Google Meet-capable node

On the node host:

apus plugins install @openclaw/google-meet
apus plugins enable browser
APUS_ALLOW_INSECURE_PRIVATE_WS=1 \
  apus node run --host <gateway-lan-ip> --port 18789 --display-name parallels-macos

On the Gateway host:

apus devices list
apus devices approve <requestId>
apus nodes status

The node must be connected and list googlemeet.chrome plus browser.proxy; the Gateway config must allow both:

{
  gateway: {
    nodes: {
      commands: { allow: ["browser.proxy", "googlemeet.chrome"] },
    },
  },
}

If googlemeet setup fails chrome-node-connected, or the Gateway log reports gateway token mismatch, reinstall or restart the node with the current Gateway token:

APUS_ALLOW_INSECURE_PRIVATE_WS=1 \
  apus node install \
  --host <gateway-lan-ip> \
  --port 18789 \
  --display-name parallels-macos \
  --force

Then reload the node service and re-run:

apus googlemeet setup
apus nodes status --connected

Browser opens but agent cannot join

Run googlemeet test-listen for observe-only joins or googlemeet test-speech for realtime joins, then inspect the returned Chrome health. If either includes manualAction, show manualAction.message to the operator and stop retrying until the browser action is complete.

Common manual actions: sign in to the Chrome profile; admit the guest from the Meet host account; grant Chrome microphone/camera permissions when the native prompt appears; close or repair a stuck Meet permission dialog.

Do not report "not signed in" just because Meet asks "Do you want people to hear you in the meeting?"; that is Meet's audio-choice interstitial. Apus clicks Use microphone through browser automation when available and keeps waiting for the real meeting state; for create-only browser fallback it may click Continue without microphone instead, since minting the URL does not need the realtime audio path.

Meeting creation fails

googlemeet create uses the Meet API spaces.create when OAuth is configured, otherwise the pinned Chrome node browser. Confirm:

Agent joins but does not talk

apus googlemeet setup
apus googlemeet doctor

Use mode: "agent" for the STT -> Apus agent -> TTS path, or mode: "bidi" for realtime voice, including GPT-Live. mode: "transcribe" intentionally starts no talk-back bridge. For observe-only debugging, run apus googlemeet status --json <session-id> after participants speak and check captioning, transcriptLines, lastCaptionText. If inCall is true but transcriptLines stays 0, Meet captions may be disabled, no one has spoken since the observer was installed, the Meet UI changed, or live captions are unavailable for the meeting language/account.

googlemeet test-speech checks the selected talk-back path and requires a fresh assistant-output waveform on the virtual microphone capture path. If speechOutputVerified is false and speechOutputTimedOut is true, inspect both provider output and the native injection/loopback fields; accepted output bytes alone do not prove speech reached Meet's microphone.

Also verify provider authentication on the Gateway host, the native audio backend on the Chrome host, and both audio routes in doctor. With generated input commands, browser sessions use the virtual microphone for assistant injection and report Isolated browser playback for participant input. Explicit input commands retain their configured capture path. GPT-Live with Cove uses the configured OpenAI account; see the Live configuration.

googlemeet doctor [session-id] prints session, node, in-call state, manual action reason, realtime provider connection, realtimeReady, audio input/output activity, last audio timestamps, byte counters, and browser URL. Use googlemeet status [session-id] --json for raw JSON, and googlemeet doctor --oauth (add --meeting or --create-space) to verify OAuth refresh without exposing tokens.

If an agent timed out and a Meet tab is already open, inspect it without opening another one:

apus googlemeet recover-tab
apus googlemeet recover-tab https://meet.google.com/abc-defg-hij

The equivalent tool action is recover_current_tab: it focuses and inspects an existing Meet tab for the selected transport (local browser control for chrome, the configured node for chrome-node) without opening a new tab or session, and reports the current blocker (login, admission, permissions, audio-choice state). The CLI command talks to the configured Gateway, which must be running; chrome-node also requires the node to be connected.

Live cannot hear interruptions or browser capture fails

Use bidi mode with an explicit Live model. Setting a voice model while keeping agent mode still uses regular TTS. GPT-Live handles interruptions itself, so chrome.bargeInInputCommand is not required and is not started for Live.

For Live, participant input must come from isolated browser playback. Remove an explicit chrome.audioInputCommand to select that managed path; custom commands remain provider input for other models and are not silently replaced. If the bridge reports that it requires isolated meeting audio or cannot capture browser playback, inspect the tracked Meet tab and its reported browser error. Update both Gateway and paired node when using chrome-node, then retry after browser playback is available. The bridge stops on capture failure instead of feeding assistant output back into Live. An empty room with no remote media yet can remain connected and idle.

Do not route participant playback back into the virtual microphone to work around a capture error. To verify duplex behavior, use a controlled second participant to confirm the response is audible and speak during it; local test-speech verification alone cannot prove that round trip.

Twilio setup checks fail

twilio-voice-call-plugin fails when voice-call is not allowed or not enabled: add it to plugins.allow, enable plugins.entries.voice-call, and rerun setup after the change applies.

twilio-voice-call-credentials fails when the Twilio backend is missing account SID, auth token, or caller number:

export TWILIO_ACCOUNT_SID=AC...
export TWILIO_AUTH_TOKEN=...
export TWILIO_FROM_NUMBER=+15550001234

twilio-voice-call-webhook fails when voice-call has no public webhook exposure, or publicUrl points at loopback/private network space. Do not use localhost, 127.0.0.1, 0.0.0.0, 10.x, 172.16.x-172.31.x, 192.168.x, 169.254.x, fc00::/7, or fd00::/8 as publicUrl; carrier callbacks cannot reach those. Set plugins.entries.voice-call.config.publicUrl to a public URL, or configure a tunnel/Tailscale exposure:

{
  plugins: {
    entries: {
      "voice-call": {
        enabled: true,
        config: {
          provider: "twilio",
          fromNumber: "+15550001234",
          publicUrl: "https://voice.example.com/voice/webhook",
        },
      },
    },
  },
}

For local development, use a tunnel or Tailscale exposure instead of a private host URL:

{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          tunnel: { provider: "ngrok" },
          // or
          tailscale: { mode: "funnel", path: "/voice/webhook" },
        },
      },
    },
  },
}

Plugin config changes apply automatically with the default hybrid reload mode (see Hot reload). If you changed environment-based credentials, restart the Gateway with the updated environment. Then verify:

apus googlemeet setup --transport twilio
apus voicecall setup
apus voicecall smoke

voicecall smoke is readiness-only by default. Dry-run a specific number:

apus voicecall smoke --to "+15555550123"

Only add --yes to intentionally place a live outbound call:

apus voicecall smoke --to "+15555550123" --yes

Twilio call starts but never enters the meeting

Confirm the Meet event exposes phone dial-in details, and pass the exact dial-in number plus PIN or a custom DTMF sequence:

apus googlemeet join https://meet.google.com/abc-defg-hij \
  --transport twilio \
  --dial-in-number +15551234567 \
  --dtmf-sequence ww123456#

Use leading w or commas in --dtmf-sequence for a pause before the PIN.

If the call is created but the Meet roster never shows the dial-in participant:

If webhooks do not arrive, debug the Voice Call plugin first: the provider must reach plugins.entries.voice-call.config.publicUrl or the configured tunnel. See Voice call troubleshooting.