While running the following code I receiving 503 bad gateway errors for seemingly no reason. The error output is attached after the code. I have tried creating a client from the credentials saved in the environment as well as creating the client using a service account file. I have looked at my Google Cloud Console and see that there errors are being detected, but no info about why this is. Under the "APIs & Services" section of my project, I see the following: My quota for "Count of requests for Chirp3-HD voices per minute" is at 0.5%. Any help or advice is appreciated. Thanks! #!/usr/bin/env python3 import logging import os import argparse from sys import argv, stdin, exit from random import random from typing import List from google.cloud import texttospeech from google.oauth2 import service_account from google.api_core.retry import Retry, if_transient_error logging.basicConfig(level=logging.INFO) def get_client(credentials_file: str = None) -> texttospeech.TextToSpeechClient: """Returns text to speech client object.""" if credentials_file != None: creds = service_account.Credentials.from_service_account_file(credentials_file) return texttospeech.TextToSpeechClient(credentials=creds) else: return texttospeech.TextToSpeechClient() def chunk_text(text: str, delimiter: str = "\n", max_chars: int = 5000) -> List[str]: """ Splits text into chunks divided by delimiter. Chunks are further split if more than `max_chars` characters. Splitting is done by delimiter boundaries when possible. """ sentences = text.split(delimiter) chunks, current_chunk = [], "" for sentence in sentences: candidate = current_chunk + sentence + delimiter if len(candidate) > max_chars: chunks.append(current_chunk) current_chunk = sentence + delimiter else: current_chunk = candidate if current_chunk: chunks.append(current_chunk) return chunks def generate_paths( project_name: str, num_chapters: int, extension: str = "wav", pad_width: int = 2, ) -> List[str]: """Generate paths for chapter outputs with consistent structure.""" return [ f"output/{project_name}/chapters/chapter{str(i).zfill(pad_width)}.{extension}" for i in range(1, num_chapters + 1) ] def synthesize_audio( client: texttospeech.TextToSpeechClient, project_name: str, input_text: str, speaking_rate: float = 0.85, language_code: str = "en-US", voice_name: str = "en-US-Chirp3-HD-Algenib", skip_chapters: List[int] = None ) -> List[str]: """ Splits long input into chapters and synthesizes each to a GCS bucket. Files are saved to: gs://bucket/project_name/chapters/chapterXX.wav Returns list of GCS paths for generated files. """ # Break text into manageable chunks chunks = chunk_text( text = input_text ) logging.info("Split input into %d chapters.", len(chunks)) # Generate output paths using helper chapter_paths = generate_paths(project_name, len(chunks)) audio_config = texttospeech.AudioConfig( audio_encoding = texttospeech.AudioEncoding.LINEAR16, speaking_rate = speaking_rate, ) voice = texttospeech.VoiceSelectionParams( language_code = language_code, name = voice_name, ) # Define a custom retry policy for transient errors (like 502 Bad Gateway) # This policy will retry the call with exponential backoff for up to 120 seconds. custom_retry = Retry( initial = 1.0, # The minimum amount of time to delay. maximum = 60.0, # The maximum amount of time to delay. multiplier = 2.0, # The multiplier applied to the delay. deadline = 120.0, # How long to keep retrying. predicate = if_transient_error # The default predicate for transient errors. ) for idx, (chunk, output_path) in enumerate(zip(chunks, chapter_paths), start=1): if skip_chapters != None and idx in skip_chapters: logging.info("Skipping chapter %d", idx) continue synthesis_input = texttospeech.SynthesisInput(text=chunk) logging.info("Synthesizing chapter %d", idx) response = client.synthesize_speech( input = synthesis_input, voice = voice, audio_config = audio_config, retry = custom_retry ) logging.info("Synthesizing chapter %d pt. 2", idx) try: os.makedirs(os.path.dirname(output_path), exist_ok=True) with open(output_path, "wb") as out: out.write(response.audio_content) logging.info("Saved chapter %d at %s", idx, output_path) except Exception as e: logging.error("Error on chapter %d: %s", idx, e) return chapter_paths def main(): """ Parses command-line arguments, reads input text, and synthesizes long audio files using Google Cloud Text-to-Speech. The script supports two modes of operation for input: 1. Reading from a file specified by the `--file-path` argument. 2. Reading from standard input (stdin) if no file path is provided. The output audio is split into manageable chapters and saved to the 'output' directory in WAV format. The output directory structure is `output/<project_name>/chapters/chapterXX.wav`. The `--skip-chapters` argument can be used to restart or resume a long synthesis job without reprocessing all chapters. Example usage: - Synthesize text from a file: `python tts.py --file-path "my_book.txt"` - Synthesize text from standard input: `cat "my_book_chapter.txt" | python tts.py` - Synthesize from a file, skipping chapters 1 and 3: `python tts.py --file-path "my_book.txt" --skip-chapters 1 3` """ parser = argparse.ArgumentParser( description="Synthesizes text into speech using Google Cloud TTS." ) # Optional file path for input text parser.add_argument( "--file-path", type=str, help="Path to an input text file. If not provided, reads from stdin." ) # Optional chapters to skip parser.add_argument( "--skip-chapters", type=int, nargs="+", # This allows multiple integers help="A space-separated list of chapter numbers to skip (e.g., 1 3 5)." ) args = parser.parse_args() input_text = "" if args.file_path: project_name = os.path.basename(args.file_path).rpartition(".")[0] try: with open(args.file_path, "r") as f: input_text = f.read() except FileNotFoundError: logging.error(f"Error: The file '{args.file_path}' was not found.") exit(1) else: project_name = str(int(random()*(10**32))) try: for line in stdin: input_text += line except KeyboardInterrupt: exit(1) client = get_client() #get_client(credentials_file=credentials_file) synthesize_audio( client=client, project_name=project_name, input_text=input_text, skip_chapters=args.skip_chapters ) if __name__ == "__main__": main() INFO:root:Split input into 11 chapters. INFO:root:Synthesizing chapter 1 Traceback (most recent call last): File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/grpc_helpers.py", line 76, in error_remapped_callable return callable_(*args, **kwargs) File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 277, in __call__ response, ignored_call = self._with_call( ~~~~~~~~~~~~~~~^ request, ^^^^^^^^ ...<4 lines>... compression=compression, ^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 332, in _with_call return call.result(), call ~~~~~~~~~~~^^ File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 440, in result raise self File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 315, in continuation response, call = self._thunk(new_method).with_call( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ request, ^^^^^^^^ ...<4 lines>... compression=new_compression, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 1192, in with_call return _end_unary_response_blocking(state, call, True, None) File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 1006, in _end_unary_response_blocking raise _InactiveRpcError(state) # pytype: disable=not-instantiable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with: status = StatusCode.UNAVAILABLE details = "502:Bad Gateway" debug_error_string = "UNKNOWN:Error received from peer {grpc_status:14, grpc_message:"502:Bad Gateway"}" > The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 147, in retry_target result = target() File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/grpc_helpers.py", line 78, in error_remapped_callable raise exceptions.from_grpc_error(exc) from exc google.api_core.exceptions.ServiceUnavailable: 503 502:Bad Gateway The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 218, in <module> main() ~~~~^^ File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 210, in main synthesize_audio( ~~~~~~~~~~~~~~~~^ client=client, ^^^^^^^^^^^^^^ ...<2 lines>... skip_chapters=args.skip_chapters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 117, in synthesize_audio response = client.synthesize_speech( input = synthesis_input, ...<2 lines>... retry = custom_retry ) File "/var/home/connor/.local/lib/python3.13/site-packages/google/cloud/texttospeech_v1/services/text_to_speech/client.py", line 946, in synthesize_speech response = rpc( request, ...<2 lines>... metadata=metadata, ) File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/gapic_v1/method.py", line 131, in __call__ return wrapped_func(*args, **kwargs) File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 294, in retry_wrapped_func return retry_target( target, ...<3 lines>... on_error=on_error, ) File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 156, in retry_target next_sleep = _retry_error_helper( exc, ...<6 lines>... timeout, ) File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_base.py", line 229, in _retry_error_helper raise final_exc from source_exc google.api_core.exceptions.RetryError: Timeout of 120.0s exceeded, last exception: 503 502:Bad Gateway Continue reading...