Google Calendar APIと Google Meet REST APIの検証で発生したエラーと考察

記事タイトルとURLをコピーする

G-gen の高宮です。Google Calendar APIGoogle Meet REST API を組み合わせた実装時に、いくつかのエラーが発生しました。その内容と原因の考察を紹介します。

はじめに

以前の記事「API経由でGoogle Meetの成果物作成を有効化したカレンダー予定を作成する」では、Google Calendar API と Google Meet REST API を利用し、Google Meet の成果物作成を有効化したカレンダーの予定を作成する方法を紹介しました。

blog.g-gen.co.jp

Google Meet の成果物(アーティファクト)作成を有効化したカレンダー予定を API で作成するために、以下の3つのアプローチを検証しました。

  1. Calendar API でイベントを作成する。次に、Meet REST API(google-apps-meet ライブラリ経由)でアーティファクト生成を有効化したスペースを作成する。次に、イベントとスペースを紐づける。
  2. Calendar API でイベントとスペースを作成する。次に、Meet REST API(google-api-python-client ライブラリ経由)でスペースのアーティファクト生成を有効化する。
  3. Calendar API でイベントを作成する。次に、Meet REST API(google-api-python-client ライブラリ経由)でアーティファクト生成を有効化したスペースを作成する。次に、イベントとスペースを紐づける。

それぞれのアプローチの実装方法の違いと動作結果は、以下の表の通りです。

No. 使用ライブラリ(Meet) スペースの作成 アーティファクト設定 イベントとの紐づけ 動作結果
1 google-apps-meet ライブラリ Meet REST API スペース作成時に指定 Calendar API で作成したイベントを更新し、紐づけ 会議スペース作成時にエラー
2 google-api-python-client ライブラリ Calendar API Meet REST API で作成されたスペースの設定を更新 Calendar API でイベント作成時に自動で紐づけ Meet 会議の設定編集時にエラー
3 google-api-python-client ライブラリ Meet REST API スペース作成時に指定 Calendar API で作成したイベントを更新し、紐づけ 期待した動作(以前の記事)

当記事では、上記のうちエラーが発生したケース1ケース2について、エラーの詳細、原因、および回避策を解説します。

会議スペース作成時にエラー

実装

以下のコマンドを実行して、google-apps-meet ライブラリを追加でインストールします。

uv add google-apps-meet==0.2.0

2025年11月時点でベータ版で公開されている機能を使用して、main.py に以下のコードを実装します。

import os
import datetime
  
from google.auth.transport import requests
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
  
from google.apps.meet_v2beta import SpacesServiceClient
from google.apps.meet_v2beta.types import Space, SpaceConfig, CreateSpaceRequest
  
from googleapiclient.discovery import build
  
  
def authorize() -> Credentials:
    """Calendar API と Meet API を呼び出すために OAuth 2.0 認証を行い、Credentials オブジェクトを返す"""
    
    CLIENT_SECRET_FILE = "./client_secret.json"
    credentials = None
  
    if os.path.exists("token.json"):
        credentials = Credentials.from_authorized_user_file("token.json")
  
    if credentials is None:
        flow = InstalledAppFlow.from_client_secrets_file(
            CLIENT_SECRET_FILE,
            scopes=[
                "https://www.googleapis.com/auth/calendar.events.owned",
                "https://www.googleapis.com/auth/meetings.space.created",
            ],
        )
        flow.run_local_server(port=0)
        credentials = flow.credentials
  
    if credentials and credentials.expired:
        credentials.refresh(requests.Request())
  
    if credentials is not None:
        with open("token.json", "w") as f:
            f.write(credentials.to_json())
  
    return credentials
  
  
USER_CREDENTIALS = authorize()
  
  
def create_event():
    """Calendar API を使用してイベントを作成する"""
  
    # サービスオブジェクトの構築
    service = build("calendar", "v3", credentials=USER_CREDENTIALS)
  
    # 指定した時刻から1時間の予定を作成
    now = datetime.datetime.now()
    startTime = now.isoformat()
    endTime = (now + datetime.timedelta(hours=1)).isoformat()
    event = {
        "summary": "Google Calendar Meet Test Event",
        "start": {
            "dateTime": startTime,
            "timeZone": "Asia/Tokyo",
        },
        "end": {
            "dateTime": endTime,
            "timeZone": "Asia/Tokyo",
        },
    }
    # 自身のカレンダーに予定を追加
    event = service.events().insert(calendarId="primary", body=event).execute()
  
    return event
  
  
def create_space() -> Space:
    """Meet API を使用して アーティファクト生成を有効化した Space リソースを作成する"""
  
    # クライアントを作成し、 Space を作成
    client = SpacesServiceClient(credentials=USER_CREDENTIALS)
    request = CreateSpaceRequest()
    space = client.create_space(request=request)
  
    # artifact の自動生成を有効化
    space.config = SpaceConfig(
        artifactConfig=SpaceConfig.ArtifactConfig(
            recordingConfig=SpaceConfig.ArtifactConfig.RecordingConfig(
                autoRecordingGeneration=SpaceConfig.ArtifactConfig.RecordingConfig.AutoRecordingGeneration.ON
            ),
            transcriptionConfig=SpaceConfig.ArtifactConfig.TranscriptionConfig(
                autoTranscriptionGeneration=SpaceConfig.ArtifactConfig.TranscriptionConfig.AutoTranscriptionGeneration.ON,
            ),
            smartNotesConfig=SpaceConfig.ArtifactConfig.SmartNotesConfig(
                autoSmartNotesGeneration=SpaceConfig.ArtifactConfig.SmartNotesConfig.AutoSmartNotesGeneration.ON,
            ),
        ),
    )
  
    return space
  
  
def update_event(eventId: str = None, meetUri: str = None):
    """Calendar API を使用して既存のイベントに Meet 情報を追加する"""

    # サービスオブジェクトの構築
    service = build("calendar", "v3", credentials=USER_CREDENTIALS)

    # 予定の更新
    event = service.events().get(calendarId="primary", eventId=eventId).execute()
    event["conferenceData"] = {
        "conferenceSolution": {
            "key": {"type": "hangoutsMeet"},
        },
        "entryPoints": [
            {
                "entryPointType": "video",
                "uri": meetUri,
            }
        ],
    }
  
    updated_event = (
        service.events()
        .update(
            calendarId="primary",
            eventId=event["id"],
            body=event,
            conferenceDataVersion=1,
        )
        .execute()
    )
  
    return updated_event
  
  
def main():
    """メイン処理"""
  
    # イベントと Space の作成
    event = create_event()
    print(f"Google Calendar URL {event.get('htmlLink')}")
    space = create_space()
  
if __name__ == "__main__":
    main()

以下の関数は以前の記事と同様です。

  • authorize
  • create_event
  • update_event

以前の記事と異なるのは、create_space 関数における以下の処理の実装です。

  1. 認証情報を使用して、SpacesServiceClient クラスのインスタンスを作成します。
  2. CreateSpaceRequest クラスのインスタンスを引数に設定し、create_space メソッドを呼び出しスペースを作成するリクエストを送信します。
  3. 処理 2. で作成したスペースに対して、以下のクラスを使用して Google Meet の成果物作成を有効化します。
クラス名 説明
SpaceConfig 会議スペース全般の構成
ArtifactConfig 会議でサポートされている自動生成アーティファクトに関する構成
RecordingConfig 録画の構成
TranscriptionConfig 自動文字起こしの構成
SmartNotesConfig 自動スマートメモの構成

エラー

処理 2. の実行時(80行目)に、以下の例外が発生します。

例外が発生しました: MethodNotImplemented
501 Method not found.
grpc.channel.InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.UNIMPLEMENTED
details = "Method not found."
debug_error_string = "UNKNOWN:Error received from peer ipv4:{IPアドレス}:443 {grpc_message:"Method not found.", grpc_status:12}"

原因

エラーメッセージに、gRPC の標準的なステータスコードである StatusCode.UNIMPLEMENTED(エラーコード 12)が返却されており、「リクエストされた操作が実装されていない、または API によってサポートされていない」ことがわかります。

ベータ版の機能であるため、サーバー側で当該メソッドがまだ実装されていない、あるいは公開されていないことが原因と推測され、本番環境での使用には十分な注意が必要です。

回避策

処理 2.google-api-python-client ライブラリ経由で Meet REST API を呼び出す実装に変更することで期待した動作になります。

Meet 会議の設定編集時にエラー

実装

main.py に以下のコードを実装します。本実装では、API の呼び出しは、google-api-python-client ライブラリを使用して行っています。

import os
import datetime
import uuid
  
from google.auth.transport import requests
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
  
from googleapiclient.discovery import build
  
  
def authorize() -> Credentials:
    """Calendar API と Meet API を呼び出すために OAuth 2.0 認証を行い、Credentials オブジェクトを返す"""
    
    CLIENT_SECRET_FILE = "./client_secret.json"
    credentials = None
  
    if os.path.exists("token.json"):
        credentials = Credentials.from_authorized_user_file("token.json")
  
    if credentials is None:
        flow = InstalledAppFlow.from_client_secrets_file(
            CLIENT_SECRET_FILE,
            scopes=[
                "https://www.googleapis.com/auth/calendar.events.owned",
                "https://www.googleapis.com/auth/meetings.space.created",
                "https://www.googleapis.com/auth/meetings.space.settings",
            ],
        )
        flow.run_local_server(port=0)
        credentials = flow.credentials
  
    if credentials and credentials.expired:
        credentials.refresh(requests.Request())
  
    if credentials is not None:
        with open("token.json", "w") as f:
            f.write(credentials.to_json())
  
    return credentials
  
  
USER_CREDENTIALS = authorize()
  
  
def create_event():
    """Calendar API を使用してイベントを作成する"""
  
    # サービスオブジェクトの構築
    service = build("calendar", "v3", credentials=USER_CREDENTIALS)
  
    # 指定した時刻から1時間の予定を作成
    now = datetime.datetime.now()
    startTime = now.isoformat()
    endTime = (now + datetime.timedelta(hours=1)).isoformat()
    event = {
        "summary": "Google Calendar Meet Test Event",
        "start": {
            "dateTime": startTime,
            "timeZone": "Asia/Tokyo",
        },
        "end": {
            "dateTime": endTime,
            "timeZone": "Asia/Tokyo",
        },
        "conferenceData": {
            "createRequest": {
                "requestId": str(uuid.uuid4()),
                "conferenceSolutionKey": {"type": "hangoutsMeet"},
            }
        },
        
    }
    # 自身のカレンダーに予定を追加
    event = service.events().insert(calendarId="primary", body=event, conferenceDataVersion=1).execute()
  
    return event
  
  
def update_space(meetUri: str = None):
    """Meet API を使用して 既存の Meet 会議をアーティファクト生成を有効化した Space リソースに更新する"""
  
    # google-api-python-client での実装
    service = build("meet", "v2", credentials=USER_CREDENTIALS)
  
    # meetUri から Space 名を抽出
    name = f"spaces/{meetUri.split('/')[-1]}"
  
    # 変更する Space リソースのボディ定義
    space_body = {
        "config": {
            "artifactConfig": {
                "recordingConfig": {
                    "autoRecordingGeneration": "ON",
                },
                "transcriptionConfig": {
                    "autoTranscriptionGeneration": "ON",
                },
                "smartNotesConfig": {
                    "autoSmartNotesGeneration": "ON",
                },
            }
        }
    }
  
    # 更新する対象の定義
    update_mask = "config.artifactConfig.recordingConfig,config.artifactConfig.transcriptionConfig,config.artifactConfig.smartNotesConfig"
  
    # Space リソースの更新
    return service.spaces().patch(name=name, body=space_body, updateMask=update_mask).execute()
  
  
def main():
    """メイン処理"""
  
    # Meet 会議 を含んだイベントの作成
    event = create_event()
    print(f"Google Calendar URL {event.get('htmlLink')}")
    meet_uri = event["conferenceData"]["entryPoints"][0]["uri"]
    print(f"Meet URL {meet_uri}")
    
    # Space の設定を更新
    update_space(meetUri=meet_uri)
  
  
if __name__ == "__main__":
    main()

以前の記事との相違点は以下です。

  • authorize 関数の scopes で、https://www.googleapis.com/auth/meetings.space.settings を指定しています。このスコープを指定することで、Google Meet 通話すべての設定の表示および編集が可能です。
  • create_event 関数で conferenceData フィールドを使用して、Meet 会議の設定を追加します。insert メソッド呼び出し時の引数として、conferenceDataVersion=1 を追加し、Meet 会議を含むイベントを作成します。

以前の記事と異なるのは、update_space 関数における以下の実装です。

  1. イベントに紐づく Meet 会議の URI を引数に取得し、https://meet.google.com/{meetingCode}spaces/{meetingCode} の形式に変換します。
  2. Google Meet の成果物作成を有効化する設定を JSON 形式で定義します。
  3. 更新するフィールド情報を文字列で定義します。JSON のネストはドット(.)区切り、複数のフィールドを指定したい場合は、カンマ(,)区切りで表現します。
  4. 処理 1.2.3. で定義した変数を引数に patch メソッドを実行することで、Meet 会議の設定を更新します。

エラー

処理 3. の実行時(110行目)に、以下の例外が発生します。

例外が発生しました: HttpError
<HttpError 403 when requesting https://meet.googleapis.com/v2/spaces/{meetingCode}?updateMask=config.artifactConfig.recordingConfig%2Cconfig.artifactConfig.transcriptionConfig%2Cconfig.artifactConfig.smartNotesConfig&alt=json returned "Permission denied on resource Space (or it might not exist)". Details: "Permission denied on resource Space (or it might not exist)">

原因

エラーメッセージに、HTTP のレスポンスステータスコードの 403 が返却されており、「リクエストした操作を行う権限がない」ことがわかります。

Meet REST API の仕様として、自身で作成したスペースに対する更新のみを許容しており、カレンダーなどの外部の操作で作成されたスペースを更新できないことが原因と推測されます。

回避策

以下の実装に変更することで、期待した動作になります。

  1. create_event 関数 では、イベント作成時に Meet 会議が作成されないように設定します。
  2. google-api-python-client ライブラリ経由で Meet REST API を呼び出し、アーティファクト生成が有効化されたスペースを作成します。
  3. Calendar API を使用して、作成したイベントとスペースを紐づけします。

高宮 怜(記事一覧)

クラウドソリューション部クラウドエクスプローラ課

2025年6月より、G-genにジョイン。前職は四国のSIerで電力、製造業系のお客様に対して、PM/APエンジニアとして、要件定義から運用保守まで全工程を担当。現在はGoogle Cloudを学びながら、フルスタックエンジニアを目指してクラウドエンジニアとしてのスキルを習得中。