Dev.to WebDev 🛠 Dev 👁 0 📖 2 min read

My Gemini calls failed from Korea with "User location is not supported". A Durable Object fixed it.

I’m building hihu, an app for learning how to prompt AI. It’s just me working on it, from Korea. Each lesson includes a chat with Gemini, with the requests going through a Cloudflare Worker so the API key stays out of th

I’m building hihu, an app for learning how to prompt AI. It’s just me working on it, from Korea. Each lesson includes a chat with Gemini, with the requests going through a Cloudflare Worker so the API key stays out of the app.

While testing on my phone, I kept getting this error:

400 User location is not supported

About half my messages failed. I could send a sentence, get the error, then send the exact same sentence again and get a normal reply.

It turned out to depend on where the request went through Cloudflare. My requests from Korea were going through either Hong Kong or Tokyo. The Tokyo requests worked. The Hong Kong ones failed because the Gemini API isn’t available there.

Placement didn’t fix it

I first tried the [placement] setting in wrangler.toml, hoping it would move the Worker somewhere Gemini would accept requests from.

The cf-placement response header showed remote-OMA, which suggested the Worker was running in Omaha. That looked promising, but the Gemini calls were still leaving from Hong Kong and failing.

What finally worked

I moved the Gemini call into a Durable Object and gave it a locationHint of wnam, for western North America. The Worker now passes requests to that object instead of calling Gemini itself.

Here’s the relevant config:

[[durable_objects.bindings]]
name = "US_EXIT"
class_name = "UsExit"

[[migrations]]
tag = "v1"
new_sqlite_classes = ["UsExit"]

The object doesn’t do much. It takes the request body and forwards it to Gemini:

export class UsExit {
  constructor(state, env) {
    this.env = env;
  }

  async fetch(request) {
    return fetch('https://generativelanguage.googleapis.com/v1beta/interactions', {
      method: 'POST',
      headers: {
        'x-goog-api-key': this.env.GEMINI_API_KEY,
        'content-type': 'application/json',
      },
      body: await request.text(),
    });
  }
}

Then, in the Worker:

const exit = env.US_EXIT.get(
  env.US_EXIT.idFromName('us'),
  { locationHint: 'wnam' }
);

const response = await exit.fetch('https://exit/', {
  method: 'POST',
  body: JSON.stringify(body),
});

Before this change, all 12 requests I tested through Hong Kong failed. After the change, all 12 succeeded. Replies took about four seconds.

That’s a small test, but it fixed the issue I was seeing on my phone.

A few details if you’re trying this yourself:

  • This works on the free plan with SQLite-backed Durable Objects (new_sqlite_classes in the config).
  • The location hint is a preference, not a guarantee, and it only applies when the object is first created. Adding it to an existing object won’t move that object.
  • Using idFromName('us') sends everything through the same object. That’s enough for my app right now. I haven’t tested it under heavy traffic.

The app is hihu. There are only two lessons so far, both in English, and each takes a few minutes. This was the bug I had to sort out to get the chat working reliably.
!I built hihu with a lot of help from AI coding tools, and AI helped me write this post in English.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.