Local debugging guide for Extend apps
Overview
This guide covers the debugging concepts and tools that are common to all AccelByte Extend app types — Extend Override, Extend Event Handler, and Extend Service Extension.
Use this as a baseline reference. For details specific to your app type or language, use the navigation table below.
Navigate by app type and language
By app type
| App type | Specific debugging guide |
|---|---|
| Extend Override | Coming soon |
| Extend Event Handler | Coming soon |
| Extend Service Extension | Local debugging guide for Extend Service Extension |
By language
| Language | Language setup guide |
|---|---|
| Go | Go |
| C# | C# |
| Java | Java |
| Python | Python |
Environment setup
Every Extend app reads its configuration from environment variables. A template file is provided in the repository root.
Step 1 — Create your .env file
cp .env.template .env
Then open .env in your editor and fill in the values.
The repository ships with a VS Code task called "Create .env File" (see .vscode/tasks.json).
If you have fillenv installed, this task can populate the file for you automatically.
Step 2 — Fill in the required variables
The following variables are required by every Extend app type:
# AccelByte environment
AB_BASE_URL=https://<your-ags-environment>.accelbyte.io
AB_CLIENT_ID=<your-client-id>
AB_CLIENT_SECRET=<your-client-secret>
# Disable token validation during local development.
# Deployed Extend apps always have this value set to `true` and can't be changed.
PLUGIN_GRPC_SERVER_AUTH_ENABLED=false
# Log verbosity: debug | info | warn | error
LOG_LEVEL=debug
| Variable | Purpose | Example |
|---|---|---|
AB_BASE_URL | Your AGS environment URL | https://dev.accelbyte.io |
AB_CLIENT_ID | OAuth client ID | abc123 |
AB_CLIENT_SECRET | OAuth client secret | s3cr3t |
PLUGIN_GRPC_SERVER_AUTH_ENABLED | false to skip token checks locally | false |
LOG_LEVEL | Log verbosity | debug |
Some app types require additional variables. For example, Extend Service Extension requires
BASE_PATH. See the app type-specific debugging guide for the complete variable list.
Step 3 — Confirm the service can reach AGS
curl "$AB_BASE_URL/iam/v3/public/config"
A JSON response confirms the URL is reachable. An error here means the URL is wrong or unreachable.
Running the service locally
From VS Code
Use Terminal → Run Task → "Run: Service".
This task is defined in .vscode/tasks.json and loads the .env file automatically.
Confirming the service is up
Once the service starts you should see log output similar to:
{"time":"...","level":"INFO","msg":"app server started"}
For the exact terminal run command for your language, see the language-specific guide.
Attaching the debugger in VS Code
The repository ships with a ready-to-use launch configuration in .vscode/launch.json.
- Make sure your
.envfile is filled in. - Open the Run and Debug panel — press
Ctrl+Shift+D(Windows/Linux) orCmd+Shift+D(macOS), or click the bug icon in the Activity Bar. - Select "Debug: Service" from the dropdown at the top of the panel.
- Press F5 (or click the green ▶ button).
VS Code starts the service inside the debugger. You can now pause execution, inspect variables, and step through code line by line.
For language-specific launch configuration details and non-VS Code setups, see the language-specific guide.
Setting breakpoints and inspecting state
How to set a breakpoint
Click in the gutter — the narrow strip to the left of the line numbers — next to the line you want to pause on. A red circle appears. When execution reaches that line, VS Code pauses and shows you:
- Variables — all local variables and their current values.
- Watch — expressions you add manually to monitor continuously.
- Call Stack — every function call that led to the current line.
- Debug Console — a prompt where you can evaluate expressions live.
Stepping through code
| Action | Shortcut | What it does |
|---|---|---|
| Continue | F5 | Run until the next breakpoint |
| Step Over | F10 | Execute the current line without entering any function it calls |
| Step Into | F11 | Enter the function called on the current line |
| Step Out | Shift+F11 | Finish the current function and return to the caller |
| Restart | Ctrl+Shift+F5 | Restart the debug session |
| Stop | Shift+F5 | Stop the debugger |
Conditional breakpoints
Right-click a breakpoint circle → Edit Breakpoint → type a condition.
The debugger only pauses when the condition evaluates to true.
This is especially useful when you want to inspect only a specific request out of many.
For condition syntax examples in each language, see the language-specific guide.
Tips
-
Read the Call Stack panel. When the debugger pauses, the Call Stack shows every function call that led to the current line — from the current frame down to the entry point. This tells you exactly how a request arrived at your code.
-
Use the Watch panel. Add expressions like
req.Namespaceorerrso you can monitor them as you step, without re-expanding the Variables panel at every line. -
Use conditional breakpoints. Rather than stopping on every request, narrow the condition to the exact input you are investigating.
Reading and understanding logs
Every Extend app emits structured JSON log lines to stdout. A single log line looks like:
{"time":"2026-03-10T12:00:00Z","level":"INFO","msg":"request received","method":"SomeMethod","duration":"1.234ms"}
Log levels
| Level | When it appears | When to pay attention |
|---|---|---|
DEBUG | Only when LOG_LEVEL=debug | Fine-grained tracing of logic and variable values |
INFO | Default | Normal events (server started, request received) |
WARN | Something unexpected but non-fatal | Worth investigating if it appears repeatedly |
ERROR | Something failed | Always investigate |
Always set LOG_LEVEL=debug in your .env during local development. The extra gRPC payload
logs often reveal the problem without needing a breakpoint at all.
gRPC request/response pairs
The service automatically logs the start and finish of every gRPC call:
{"msg":"started call","grpc.method":"SomeMethod", ...}
{"msg":"finished call","grpc.code":"OK","grpc.duration":"2ms", ...}
If grpc.code is anything other than OK — for example Unauthenticated, Internal, or
NotFound — scroll up in the logs to find the ERROR line that explains why.
Pretty-printing logs with jq
Install jq and pipe the service output through it during local development:
# Pretty-print all logs
<your-run-command> 2>&1 | jq '.'
# Show only ERROR-level logs
<your-run-command> 2>&1 | jq 'select(.level == "ERROR")'
Replace <your-run-command> with the language-specific command from the
language guide.
Testing with grpcurl
grpcurl lets you call the gRPC server on port
6565 directly. Every Extend app exposes a gRPC server on this port.
# List available services (server reflection is enabled)
grpcurl -plaintext localhost:6565 list
# Call a method directly
grpcurl -plaintext \
-d '{"namespace":"mygame"}' \
localhost:6565 <ServiceName>/<MethodName>
This is useful for isolating whether a problem is in the app logic or in a higher-level gateway layer.
When PLUGIN_GRPC_SERVER_AUTH_ENABLED=true, add a valid bearer token in the gRPC metadata:
grpcurl -plaintext \
-H "authorization: Bearer <token>" \
-d '{"namespace":"mygame"}' \
localhost:6565 <ServiceName>/<MethodName>
Common issues
Service exits — "unable to login using clientId and clientSecret"
Symptom:
{"level":"ERROR","msg":"error unable to login using clientId and clientSecret","error":"..."}
Cause: AB_CLIENT_ID or AB_CLIENT_SECRET is wrong, or AB_BASE_URL points to the wrong
environment.
Fix:
- Double-check the credentials in your
.env. - Confirm
AB_BASE_URLis reachable:curl "$AB_BASE_URL/iam/v3/public/config". - Make sure your OAuth client has the
CLIENT_CREDENTIALSgrant type enabled in the Admin Portal.
All requests return 401 Unauthenticated
Symptom: Every gRPC call returns status Unauthenticated.
Cause: Token validation is enabled and the bearer token in your request is missing, expired, or lacks the required permission.
Fix (local debugging): Set PLUGIN_GRPC_SERVER_AUTH_ENABLED=false in .env and restart.
Breakpoints are never hit
Possible causes and fixes:
| Cause | Fix |
|---|---|
| The request fails before reaching your code (e.g., at auth) | Disable auth: PLUGIN_GRPC_SERVER_AUTH_ENABLED=false |
| Another instance of the service is running on the same port | Check for port conflicts: ss -tlnp | grep 6565 and stop the stale process |
| The code path is not reached (wrong method name) | Confirm available methods: grpcurl -plaintext localhost:6565 list |
Tips and best practices
-
Always use
LOG_LEVEL=debuglocally. The extra gRPC payload logs often reveal the problem without needing a breakpoint at all. -
Disable auth locally; re-enable it before you commit.
PLUGIN_GRPC_SERVER_AUTH_ENABLED=falseis a development shortcut — not a permanent setting. Before pushing, set it back totrueand verify your token permissions still work. -
Check ports before starting. If you see "address already in use", a previous instance of the service is still running:
ss -tlnp | grep 6565Kill the stale process and start again.
-
Never commit your
.envfile. It contains credentials. Confirm it is listed in.gitignore. Do commit changes to.env.templateso teammates know what variables are needed.
Debugging with AI assistance
If your team does not use AI tooling, skip this section. Every other section in this guide is fully self-contained.
AI coding assistants such as Claude Code, GitHub Copilot, and others can help explain unfamiliar code, parse logs, and suggest targeted fixes.
Effective prompting tips
Paste the full error, not just the symptom.
Instead of: "Why does my service crash?"
Try: "My Extend app exits at startup with the following log. What is the most likely cause and how do I fix it?"
{"level":"ERROR","msg":"error unable to login using clientId and clientSecret","error":"401 Unauthorized"}
Include the relevant source file.
"Here is my service implementation file. The method returns
codes.Internalfor a specific input. Should I handle this case separately?"
Ask for an explanation before a fix.
"Explain what
PLUGIN_GRPC_SERVER_AUTH_ENABLEDdoes and why it is safe to disable during local development."
MCP servers
The app templates ship with Model Context Protocol (MCP) server configurations in
.vscode/mcp.json that give AI assistants direct access to AccelByte-specific knowledge:
| Server | What it provides |
|---|---|
| ags-extend-sdk-mcp-server | Knowledge of AccelByte Extend SDK symbols, types, and usage patterns |
| ags-api-mcp-server | Live AGS REST API access (requires AB_BASE_URL in your environment) |