10 KiB
Go Version Upgrades
Last Updated: 2026-02-12
The Short Version
When Charon upgrades to a new Go version, your development tools (like golangci-lint) break. Here's how to fix it:
# Step 1: Pull latest code
git pull
# Step 2: Update your Go installation
.github/skills/scripts/skill-runner.sh utility-update-go-version
# Step 3: Rebuild tools
./scripts/rebuild-go-tools.sh
# Step 4: Restart your IDE
# VS Code: Cmd/Ctrl+Shift+P → "Developer: Reload Window"
That's it! Keep reading if you want to understand why.
What's Actually Happening?
The Problem (In Plain English)
Think of Go tools like a Swiss Army knife. When you upgrade Go, it's like switching from metric to imperial measurements—your old knife still works, but the measurements don't match anymore.
Here's what breaks:
- Renovate updates the project to Go 1.26.0
- Your tools are still using Go 1.25.6
- Pre-commit hooks fail with confusing errors
- Your IDE gets confused and shows red squiggles everywhere
Why Tools Break
Development tools like golangci-lint are compiled programs. They were built with Go 1.25.6 and expect Go 1.25.6's features. When you upgrade to Go 1.26.0:
- New language features exist that old tools don't understand
- Standard library functions change
- Your tools throw errors like:
undefined: someNewFunction
The Fix: Rebuild tools with the new Go version so they match your project.
Step-by-Step Upgrade Guide
Step 1: Know When an Upgrade Happened
Renovate (our automated dependency manager) will open a PR titled something like:
chore(deps): update golang to v1.26.0
When this gets merged, you'll need to update your local environment.
Step 2: Pull the Latest Code
cd /projects/Charon
git checkout development
git pull origin development
Step 3: Update Your Go Installation
Option A: Use the Automated Skill (Recommended)
.github/skills/scripts/skill-runner.sh utility-update-go-version
This script:
- Detects the required Go version from
go.work - Downloads it from golang.org
- Installs it to
~/sdk/go{version}/ - Updates your system symlink to point to it
- Rebuilds your tools automatically
Option B: Manual Installation
If you prefer to install Go manually:
- Go to go.dev/dl
- Download the version mentioned in the PR (e.g., 1.26.0)
- Install it following the official instructions
- Verify:
go versionshould show the new version - Continue to Step 4
Step 4: Rebuild Development Tools
Even if you used Option A (which rebuilds automatically), you can always manually rebuild:
./scripts/rebuild-go-tools.sh
This rebuilds:
- golangci-lint — Pre-commit linter (critical)
- gopls — IDE language server (critical)
- govulncheck — Security scanner
- dlv — Debugger
Duration: About 30 seconds
Output: You'll see:
🔧 Rebuilding Go development tools...
Current Go version: go version go1.26.0 linux/amd64
📦 Installing golangci-lint...
✅ golangci-lint installed successfully
📦 Installing gopls...
✅ gopls installed successfully
...
✅ All tools rebuilt successfully!
Step 5: Restart Your IDE
Your IDE caches the old Go language server (gopls). Reload to use the new one:
VS Code:
- Press
Cmd/Ctrl+Shift+P - Type "Developer: Reload Window"
- Press Enter
GoLand or IntelliJ IDEA:
- File → Invalidate Caches → Restart
- Wait for indexing to complete
Step 6: Verify Everything Works
Run a quick test:
# This should pass without errors
go test ./backend/...
If tests pass, you're done! 🎉
Troubleshooting
Error: "golangci-lint: command not found"
Problem: Your $PATH doesn't include Go's binary directory.
Fix:
# Add to ~/.bashrc or ~/.zshrc
export PATH="$PATH:$(go env GOPATH)/bin"
# Reload your shell
source ~/.bashrc # or source ~/.zshrc
Then rebuild tools:
./scripts/rebuild-go-tools.sh
Error: Pre-commit hook still failing
Problem: Pre-commit is using a cached version of the tool.
Fix 1: Let the hook auto-rebuild
The pre-commit hook detects version mismatches and rebuilds automatically. Just commit again:
git commit -m "your message"
# Hook detects mismatch, rebuilds tool, and retries
Fix 2: Manual rebuild
./scripts/rebuild-go-tools.sh
git commit -m "your message"
Error: "package X is not in GOROOT"
Problem: Your project's go.work or go.mod specifies a Go version you don't have installed.
Check required version:
grep '^go ' go.work
# Output: go 1.26.0
Install that version:
.github/skills/scripts/skill-runner.sh utility-update-go-version
IDE showing errors but code compiles fine
Problem: Your IDE's language server (gopls) is out of date.
Fix:
# Rebuild gopls
go install golang.org/x/tools/gopls@latest
# Restart IDE
# VS Code: Cmd/Ctrl+Shift+P → "Developer: Reload Window"
"undefined: someFunction" errors
Problem: Your tools were built with an old Go version and don't recognize new standard library functions.
Fix:
./scripts/rebuild-go-tools.sh
Frequently Asked Questions
How often do Go versions change?
Go releases two major versions per year:
- February (e.g., Go 1.26.0)
- August (e.g., Go 1.27.0)
Plus occasional patch releases (e.g., Go 1.26.1) for security fixes.
Bottom line: Expect to run ./scripts/rebuild-go-tools.sh 2-3 times per year.
Do I need to rebuild tools for patch releases?
Usually no, but it doesn't hurt. Patch releases (like 1.26.0 → 1.26.1) rarely break tool compatibility.
Rebuild if:
- Pre-commit hooks start failing
- IDE shows unexpected errors
- Tools report version mismatches
Why don't CI builds have this problem?
CI environments are ephemeral (temporary). Every workflow run:
- Starts with a fresh container
- Installs Go from scratch
- Installs tools from scratch
- Runs tests
- Throws everything away
Local development has persistent tool installations that get out of sync.
Can I use multiple Go versions on my machine?
Yes! Go officially supports this via golang.org/dl:
# Install Go 1.25.6
go install golang.org/dl/go1.25.6@latest
go1.25.6 download
# Install Go 1.26.0
go install golang.org/dl/go1.26.0@latest
go1.26.0 download
# Use specific version
go1.25.6 version
go1.26.0 test ./...
But for Charon development, you only need one version (whatever's in go.work).
What if I skip an upgrade?
Short answer: Your local tools will be out of sync, but CI will still work.
What breaks:
- Pre-commit hooks fail (but will auto-rebuild)
- IDE shows phantom errors
- Manual
go testmight fail locally - CI is unaffected (it always uses the correct version)
When to catch up:
- Before opening a PR (CI checks will fail if your code uses old Go features)
- When local development becomes annoying
Should I keep old Go versions installed?
No need. The upgrade script preserves old versions in ~/sdk/, but you don't need to do anything special.
If you want to clean up:
# See installed versions
ls ~/sdk/
# Remove old versions
rm -rf ~/sdk/go1.25.5
rm -rf ~/sdk/go1.25.6
But they only take ~400MB each, so cleanup is optional.
Why doesn't Renovate upgrade tools automatically?
Renovate updates Dockerfile and go.work, but it can't update tools on your machine.
Think of it like this:
- Renovate: "Hey team, we're now using Go 1.26.0"
- Your machine: "Cool, but my tools are still Go 1.25.6. Let me rebuild them."
The rebuild script bridges that gap.
What's the difference between go.work, go.mod, and my system Go?
go.work — Workspace file (multi-module projects like Charon)
- Specifies minimum Go version for the entire project
- Used by Renovate to track upgrades
go.mod — Module file (individual Go modules)
- Each module (backend, tools) has its own
go.mod - Inherits Go version from
go.work
System Go (go version) — What's installed on your machine
- Must be >= the version in
go.work - Tools are compiled with whatever version this is
Example:
go.work says: "Use Go 1.26.0 or newer"
go.mod says: "I'm part of the workspace, use its Go version"
Your machine: "I have Go 1.26.0 installed"
Tools: "I was built with Go 1.25.6" ❌ MISMATCH
Running ./scripts/rebuild-go-tools.sh fixes the mismatch.
Advanced: Pre-commit Auto-Rebuild
Charon's pre-commit hook automatically detects and fixes tool version mismatches.
How it works:
-
Check versions:
golangci-lint version → "built with go1.25.6" go version → "go version go1.26.0" -
Detect mismatch:
⚠️ golangci-lint Go version mismatch: golangci-lint: 1.25.6 system Go: 1.26.0 -
Auto-rebuild:
🔧 Rebuilding golangci-lint with current Go version... ✅ golangci-lint rebuilt successfully -
Retry linting: Hook runs again with the rebuilt tool.
What this means for you:
The first commit after a Go upgrade will be slightly slower (~30 seconds for tool rebuild). Subsequent commits are normal speed.
Disabling auto-rebuild:
If you want manual control, edit scripts/pre-commit-hooks/golangci-lint-fast.sh and remove the rebuild logic. (Not recommended.)
Related Documentation
- Go Version Management Strategy — Research and design decisions
- CONTRIBUTING.md — Quick reference for contributors
- Go Official Docs — Official multi-version management guide
Need Help?
Open a Discussion if:
- These instructions didn't work for you
- You're seeing errors not covered in troubleshooting
- You have suggestions for improving this guide
Open an Issue if:
- The rebuild script crashes
- Pre-commit auto-rebuild isn't working
- CI is failing for Go version reasons
Remember: Go upgrades happen 2-3 times per year. When they do, just run ./scripts/rebuild-go-tools.sh and you're good to go! 🚀