Skip to main content

Local debugging guide for Extend Event Handler

Last updated on April 14, 2026
Based on the Extend App template

The examples and file references in this guide are based on the AccelByte Extend Event Handler app templates. If you built your own service from scratch, the concepts still apply — adjust file paths, task names, and script names to match your project.

Overview

This guide walks you through debugging an Extend Event Handler app on your local machine.

Common debugging concepts

For debugging concepts shared across all Extend app types — environment variable setup, VS Code debug workflow, log reading, and common startup errors — see the Extend local debugging guide.

The guide focuses on Visual Studio Code (VS Code), but the concepts apply to any editor or IDE.

For language-specific setup — prerequisites, project structure, run commands, and debugger configuration — jump straight to your language:


How an event flows through the service

Every Extend Event Handler app is built on the same event-driven stack regardless of language. Understanding this helps you narrow down where a problem originates:

AGS User Action (e.g., login)


AGS Kafka Topic ← AGS publishes domain events here automatically


Kafka Connect ← bridges Kafka to gRPC; managed by AGS infrastructure
│ gRPC

gRPC Server (port 6565) ← your event handler logic lives here


AccelByte AGS Services ← your handler calls back into AGS (e.g., grant an item)

Key difference from Extend Service Extension: There is no HTTP gateway in front of your service. Events arrive exclusively via Kafka Connect over gRPC. You cannot trigger your handler with a browser or curl the way you would a REST endpoint — instead you use grpcurl to simulate incoming events, or trigger the real event in AGS (e.g., log in as a test user).

Port summary:

PortPurpose
6565gRPC server — receives events from Kafka Connect and accepts grpcurl test calls
8080Prometheus metrics endpoint (/metrics)

When something goes wrong, trace the problem layer by layer:

SymptomMost likely layerWhere to look first
Service won't startEnvironment / credentials.env file, ITEM_ID_TO_GRANT, IAM client credentials
Handler is never calledKafka Connect not configured or not runningAGS Kafka Connect configuration, port 6565 reachability
Handler is called but returns an errorBusiness logic / AGS API callHandler files, entitlement / fulfillment calls
AGS API calls failWrong namespace, insufficient permissions, or bad credentialsAB_NAMESPACE, IAM client permissions
Debugger never pausesPort conflict or build modeCheck running processes on port 6565
Proto changes ignoredGenerated code staleRegenerate protobuf bindings

Environment setup

For the common variables (AB_BASE_URL, AB_CLIENT_ID, AB_CLIENT_SECRET, PLUGIN_GRPC_SERVER_AUTH_ENABLED, LOG_LEVEL) and how to create and verify your .env file, see Environment setup in the generic guide.

Extend Event Handler-specific variables

Extend Event Handler requires two additional environment variables:

# The game namespace to operate in.
AB_NAMESPACE=<your-namespace-id>

# The in-game item ID that will be granted when a user logs in.
# Must exist in a published store in the namespace above.
ITEM_ID_TO_GRANT=<item-id-from-published-store>
VariablePurposeExample
AB_NAMESPACEThe game namespace to operate inmygame
ITEM_ID_TO_GRANTItem ID to grant on login eventitem_abc123

Running the service locally

For VS Code task and terminal run instructions, see Running the service locally in the generic guide.

Confirming the service is up

Once the service starts successfully you should see log output similar to:

{"time":"...","level":"INFO","msg":"starting app server..","service":"extend-app-event-handler"}
{"time":"...","level":"INFO","msg":"gRPC reflection enabled"}
{"time":"...","level":"INFO","msg":"serving prometheus metrics","port":8080,"endpoint":"/metrics"}
{"time":"...","level":"INFO","msg":"gRPC server started"}
{"time":"...","level":"INFO","msg":"app server started"}

For the exact terminal command for your language, see the language-specific guide.


Attaching the debugger

For VS Code debugger attachment steps, see Attaching the debugger in VS Code in the generic guide.

For the Event Handler-specific .vscode/launch.json configuration and non-VS Code setups, see the language-specific guide.


Setting breakpoints and inspecting state

For how to set breakpoints, use the VS Code debug panels, step through code, and write conditional breakpoints, see Setting breakpoints and inspecting state in the generic guide.

For the recommended breakpoint locations in your Event Handler service files and language-specific conditional syntax examples, see the language-specific guide.


Triggering events for testing

Because the Event Handler receives events passively — there is no URL to POST to — you need a way to send a test event to your locally running service. You have two options:

Option 1 — Trigger a real event in AGS

Log in as a test user in your AGS environment (for example, via the Player Portal or the AGS game client SDK). If Kafka Connect is configured to forward events to your local service, the userLoggedIn event arrives in your handler automatically.

Local development without Kafka Connect

When running entirely locally (without Kafka Connect pointed at your service), events from AGS will not arrive automatically. Use Option 2 to simulate them.

Option 2 — Simulate an event with grpcurl

grpcurl is a command-line tool that lets you call gRPC services directly. Because gRPC reflection is enabled, no additional setup is required.

# Simulate a userLoggedIn event
grpcurl -plaintext \
-d '{"userId": "test-user-001", "namespace": "mygame"}' \
localhost:6565 \
accelbyte.iam.account.v1.UserAuthenticationUserLoggedInService/OnMessage

# Simulate a userThirdPartyLoggedIn event
grpcurl -plaintext \
-d '{"userId": "test-user-001", "namespace": "mygame", "platformId": "steam"}' \
localhost:6565 \
accelbyte.iam.account.v1.UserAuthenticationUserThirdPartyLoggedInService/OnMessage

A successful response is {} (an empty JSON object, which is what google.protobuf.Empty looks like over the wire). Any non-empty error response means your handler returned an error.


Reading and understanding logs

For the structured JSON log format, log levels, gRPC call pairs, and jq usage, see Reading and understanding logs in the generic guide.

For the language-specific jq pipe command, see the language-specific guide.

Tracing a single event end-to-end

Every OnMessage call attaches a traceID field to every log line it emits. After sending a grpcurl request, filter by that trace ID to see all log lines from that one event:

# Replace <your-run-command> with the language-specific command from the language guide
<your-run-command> 2>&1 | jq -r 'select(.traceID != null) | "\(.traceID) \(.level) \(.msg)"'

Common issues

For common issues that apply to all Extend apps — credential errors, 401 Unauthenticated, and breakpoints that are never hit — see Common issues in the generic guide.

The issues below are specific to Extend Event Handler.

Service fails to start — ITEM_ID_TO_GRANT not set

Symptom:

{"level":"ERROR","msg":"ITEM_ID_TO_GRANT environment variable is required"}

Cause: The ITEM_ID_TO_GRANT environment variable is missing from the .env file.

Fix: Add ITEM_ID_TO_GRANT=<your-item-id> to .env and restart. The item ID must exist in a published store in the namespace specified by AB_NAMESPACE.


Handler is called but entitlement grant fails

Symptom:

{"level":"ERROR","msg":"finished call","grpc.code":"Internal","error":"failed to grant entitlement: ..."}

Possible causes and fixes:

CauseFix
ITEM_ID_TO_GRANT does not exist in a published storeCreate and publish an item with that ID in the AGS Admin Portal
AB_NAMESPACE does not match the namespace in the test eventMake sure AB_NAMESPACE matches the namespace field in your grpcurl payload
IAM client missing fulfillment permissionAdd ADMIN:NAMESPACE:{namespace}:USER:*:FULFILLMENT [CREATE] to the OAuth client
userId in the event does not exist in AGSUse a real user ID from the correct namespace

Handler is never invoked for real events

Symptom: A user logs in to AGS but OnMessage is never called.

Cause: Kafka Connect is not configured to forward events to your local service, or your service is not reachable from the Kafka Connect host.

Fix:

  1. During local development, use grpcurl (Option 2 above) to simulate events directly instead of relying on Kafka Connect.
  2. To receive real events, ensure Kafka Connect is configured with your service's address (host and port 6565) and that your machine is reachable from Kafka Connect. In a typical local setup, expose your service with a tunneling tool such as ngrok.

Proto changes have no effect

Symptom: You edited a .proto file but the behavior of the service did not change.

Cause: The generated code stubs have not been regenerated.

Fix: Run the "Proto: Generate" VS Code task, or run ./proto.sh directly, then restart the service.


Debugging with AI assistance

AI coding assistants such as Claude Code, GitHub Copilot, and others can act as a debugging companion — explaining unfamiliar code, parsing logs, and suggesting targeted fixes.

Using the debugging skill

The Extend Event Handler app template ships with a ready-made agent skill at .claude/skills/debugging-guide/SKILL.md. A skill is a set of instructions that tells the AI exactly how to help with a specific domain — in this case, debugging Extend apps.

The skill file is available in each language's template repository:

LanguageSKILL.md
Go.claude/skills/debugging-guide/SKILL.md

Once the skill is active, invoke it by typing one of the following in your AI chat:

IntentWhat to type
Debug a live issue/debugging-guide Go — getting Internal error on OnMessage
Write or update the debugging guide/debugging-guide write Go
Let the AI decideDescribe the problem naturally — the AI picks the right mode
Compatibility

Agent skills work out of the box with Claude Code. For other AI tools or IDE extensions, check whether they support the Agent Skills open standard. If your tool does not support skills, copy the contents of .claude/skills/debugging-guide/SKILL.md and paste it as the first message (or system prompt) in your chat session.

Below is a full skill file you can copy and save as .claude/skills/debugging-guide/SKILL.md in your own Extend Event Handler repository.

View the full debugging skill (SKILL.md)
---
name: debugging-guide
description: >
Expert guide writer and debugging assistant for AccelByte Extend Event Handler apps.
Use when a developer asks for help debugging their event handler, diagnosing startup or
runtime errors, understanding logs, setting up a debugger, or when writing or updating a
debugging guide for an Extend Event Handler app. Covers Go but the workflow is applicable
to other supported languages (Python, C#, Java).
argument-hint: "[language] [brief issue description or 'write guide']"
allowed-tools: Read, Grep, Glob, Bash(go *), Bash(dlv *), Bash(ss *), Bash(curl *), Bash(grpcurl *), Bash(jq *)
---

# Debugging Guide Skill — Extend Event Handler

You are an expert backend developer and technical writer specializing in AccelByte Gaming
Services (AGS) Extend apps. Your two modes of operation are:

1. **Debug Mode** — Help a developer diagnose and fix a real issue in their running service.
2. **Write Mode** — Author or update a debugging guide for an Extend Event Handler repository.

Detect which mode is needed from `$ARGUMENTS`. If the argument mentions a specific error,
log output, or symptom, use Debug Mode. If it mentions "write", "guide", or "document", use
Write Mode. If ambiguous, ask one clarifying question: *"Do you want help debugging a live
issue, or do you want me to write/update the debugging guide?"*

---

## Architecture Context

Every Extend Event Handler app shares this event-driven architecture:

\`\`\`
AGS User Action (e.g., login)


AGS Kafka Topic


Kafka Connect ← bridges Kafka to gRPC
│ gRPC

gRPC Server (port 6565) ← business logic lives here


AccelByte AGS Services
\`\`\`

Key environment variables:

| Variable | Purpose |
|---|---|
| `AB_BASE_URL` | AccelByte environment base URL |
| `AB_NAMESPACE` | Game namespace to operate in |
| `AB_CLIENT_ID` / `AB_CLIENT_SECRET` | OAuth client credentials |
| `ITEM_ID_TO_GRANT` | In-game item ID to grant on login events |
| `LOG_LEVEL` | `debug` \| `info` \| `warn` \| `error` |

---

## Debug Mode

### Step 1 — Identify the layer where the failure occurs

- Service fails to **start** → environment variables and IAM login.
- Handler is **never called** → Kafka Connect routing or port 6565 reachability.
- Handler returns **Internal** error → business logic, entitlement, or AGS API call.
- AGS API calls **fail** → namespace mismatch, missing permissions, or invalid item ID.
- Debugger **won't pause** → port conflicts or built without debug symbols.
- **Proto changes ignored** → regenerate bindings.

### Step 2 — Collect evidence

1. Ask for full log output at `LOG_LEVEL=debug`. Look for `"level":"ERROR"` lines.
2. Read the source files referenced in the error before suggesting a fix.
3. Check environment: `printenv | grep -E 'AB_|ITEM_ID|LOG_LEVEL'`
4. Check ports: `ss -tlnp | grep -E '6565|8080'`

### Step 3 — Common-issue checklist

| Symptom | Likely cause | Where to look |
|---|---|---|
| `ITEM_ID_TO_GRANT environment variable is required` | Missing `ITEM_ID_TO_GRANT` | `main.go` startup check |
| `unable to login using clientId and clientSecret` | Wrong credentials or unreachable `AB_BASE_URL` | `main.go` → OAuth login |
| `Internal` gRPC status on `OnMessage` | Entitlement grant failed | `pkg/service/entitlement.go`, fulfillment API |
| Handler never called for real events | Kafka Connect not routing to local service | Kafka Connect config, ngrok / tunneling |
| Breakpoints never hit | Wrong build mode or port conflict | Use `dlv debug`, check `ss -tlnp \| grep 6565` |
| Proto changes have no effect | Generated code not regenerated | Run `./proto.sh` |

### Step 4 — Suggest a minimal fix

- Explain *why* the fix works.
- Read the file first, then show the exact change.
- Provide a verification step after every fix.

### Step 5 — Verify

\`\`\`bash
# Confirm service starts cleanly
go run main.go 2>&1 | jq 'select(.level == "ERROR")'

# Confirm gRPC server is up
grpcurl -plaintext localhost:6565 list

# Simulate a login event
grpcurl -plaintext \
-d '{"userId":"test-user-001","namespace":"mygame"}' \
localhost:6565 \
accelbyte.iam.account.v1.UserAuthenticationUserLoggedInService/OnMessage
\`\`\`

---

## Write Mode

### Audience

Junior developers and game developers with limited backend experience. Avoid assuming knowledge
of Kafka, gRPC, or protobuf. Use plain language. VS Code-centric but include notes for other IDEs.

### Required sections

1. Overview + architecture diagram
2. Project structure reference (file-to-responsibility table, port table)
3. Prerequisites (language-specific)
4. Environment setup (.env, key variables, explanation of each)
5. Running the service (terminal + VS Code task)
6. Attaching the debugger (VS Code launch config, other IDEs/headless)
7. Breakpoints and inspection (placement table, stepping shortcuts, conditional breakpoints)
8. Triggering events for testing (real AGS events vs. grpcurl simulation)
9. Reading logs (jq filtering, log level table, gRPC call pairs)
10. Common issues (startup failures, handler errors, entitlement failures, Kafka Connect)
11. AI assistance (optional, skip if team doesn't use AI)

### Tone and style

- Use numbered steps for multi-step procedures.
- Use tables for port numbers, environment variables, and symptom/cause/fix mappings.
- Show terminal commands in fenced code blocks with language tags.
- Explain every `grpcurl` example — what it does, what a success looks like, what an error looks like.
- Never assume the reader knows what Kafka, gRPC, or protobuf are.

References