đ¤ AI-Hinweis: Beim Strukturieren und Formulieren dieses Beitrags habe ich ein Sprachmodell eingesetzt. Alle Befehle, Ausgaben und Versionsnummern habe ich auf meiner eigenen Maschine tatsächlich ausgefĂźhrt und ĂźberprĂźft.
TL;DR
- Claude Code lässt sich auf ein eigenes Inference-Gateway umbiegen â es sind genau 6 Umgebungsvariablen.
-
claude --versionbeweist nur, dass der Client installiert ist. Nichts weiter. đ - Die beiden Base-URLs sind nicht symmetrisch:
ANTHROPIC_BASE_URLohne/v1,OPENAI_BASE_URLmit. - Das Panel in der Desktop-App ist standardmäĂig unsichtbar â es steckt hinter dem Developer Mode.
- Der eigentliche Test ist ein echter Request mit
max_tokens=16. Alles davor ist Aufwärmen. - Nach der Konfiguration ein neues Terminal-Fenster. Im alten ist âcommand not found" das erwartete Ergebnis.
Gemessen habe ich alles auf:
| Claude Code | 2.1.283 |
| Node.js | v24.21.0 |
| OS | Windows 10 Pro 22H2 (build 19045) |
| Git | nicht installiert |
Die letzte Zeile ist kein Tippfehler. Dazu komme ich am Ende. đ
Das Problem: âinstalliert" ist nicht âläuft"
Ich wollte die Inference-Requests von Claude Code auf ein anderes Gateway umleiten. Die Konfiguration selbst war unspektakulär â ein paar Umgebungsvariablen, fertig.
Gehangen hat es danach.
Ich tippe claude --version, bekomme eine saubere Versionsnummer zurĂźck und denke: passt. đ
Nur beweist das genau eine Sache â dass der Client auf der Platte liegt. Ob der Endpoint erreichbar ist, ob der Key gĂźltig ist, ob das Modell Ăźberhaupt antwortet? Davon ist nichts geprĂźft.
Das ist der Punkt, an dem die meisten Anleitungen aufhĂśren. Und genau da fangen die Probleme an.
0ď¸âŁ Was hier technisch Ăźberhaupt passiert
Falls du das zum ersten Mal machst, kurz das Mentalmodell â ohne das sind die Fehlermeldungen später nicht lesbar.
Claude Code ist ein Client. Er muss zwei Dinge wissen: wohin der Request geht und womit er sich ausweist. Beides liest er beim Start aus dem Environment (oder, in der Desktop-App, aus den App-Settings). Dann baut er einen ganz normalen HTTPS-Request und schickt ihn los.
Mehr passiert nicht. Keine Magie, kein Daemon, keine Registrierung irgendwo.
Deshalb gibt es auch genau drei Stellen, an denen es schiefgehen kann:
- Der Wert steht nicht da, wo der Client hinschaut. â Er nimmt den Default oder gar nichts.
- Der Wert steht da, ist aber falsch formatiert. â Der Request geht raus und läuft ins Leere.
- Der Wert ist korrekt, aber die Gegenseite lehnt ab. â Key abgelaufen, Quota leer, Modell unbekannt.
Und jetzt der Clou: Die Befehle, die alle als Erstes tippen, prĂźfen ausschlieĂlich Kategorie 1 â und nicht mal die vollständig. Kategorie 2 und 3 sind fĂźr sie unsichtbar.
1ď¸âŁ Erst rausfinden, in welcher Situation du bist
claude --version
| Ausgabe | Welches Skript |
|---|---|
| Versionsnummer kommt |
configure â schreibt nur die Config |
| Command not found |
setup â installiert erst die Dependencies |
Gute Nachricht: die configure-Variante hat einen Hard-Check eingebaut. Sie sucht als Erstes claude im PATH und bricht sofort ab, wenn sie nichts findet. Falsch gewählt = nichts kaputt, das Skript verweigert einfach den Dienst. đ
Umgekehrt gilt das nicht. setup auf einer schon eingerichteten Maschine installiert die Dependencies neu. Also: diesen Schritt nicht Ăźberspringen.
2ď¸âŁ Skript laufen lassen
Claude Code ist schon da â nur konfigurieren:
curl -fsSL https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/configure.sh | bash
irm https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/windows/configure.ps1 | iex
Noch gar nichts da? Im selben Repo-Verzeichnis liegt die Full-Install-Variante â einfach configure im Befehl durch setup ersetzen (setup.sh bzw. windows/setup.ps1). Die installiert erst Git, Node.js und Claude Code und schreibt dann die Config.
Umgekehrt bitte nicht: setup auf einer schon eingerichteten Maschine installiert die Dependencies neu.
â ď¸ Ăffne die Links vorher im Browser und lies drĂźber. Das hat nichts damit zu tun, wer das Skript geschrieben hat â bei allem in der Form | bash oder | iex gehĂśrt das dazu. Die Details unten habe ich genau so aus dem Quellcode gelesen.
Das Skript fragt genau drei Dinge
| Frage | Pflicht | Verhalten |
|---|---|---|
| Token | ja | Die Eingabe wird nicht angezeigt. Nach dem EinfĂźgen bleibt der Bildschirm leer â das ist normal, nicht dreimal reinpasten đ Leer lassen = Fehler und Abbruch |
| Base URL | nein | Default vorhanden, entfernt einen ßberzähligen Slash am Ende automatisch |
| Modell | nein | Default vorhanden, später jederzeit änderbar |
Zwei Dinge, die man nur durchs Lesen des Quellcodes erfährt:
- Beim Token gibt es einen Format-Precheck (Prefix
sk-/cr-/rk-), aber ein Mismatch erzeugt nur eine Warnung und blockt nicht. Warning gesehen? Erst weiterlesen, ob danach ein echter Fehler kommt. - Die Skript-Ausgabe ist auf Englisch. Ein Bildschirm voll Englisch ist also erwartbar.
Was passiert, wenn man mittendrin abbricht?
Habe ich getestet, weil ich es wissen wollte. Ich habe das Skript nicht-interaktiv laufen lassen: Es lädt sauber runter, meldet [OK] Claude Code detected: 2.1.283, kommt zum Token-Prompt â und steigt dort mit Exit-Code 1 aus, weil in einem nicht-interaktiven Subprozess nichts zu lesen ist.
Danach habe ich alle sechs Variablen einzeln kontrolliert: keine einzige verändert. đ
Das Skript sammelt also erst alle drei Antworten und schreibt dann. Ein Abbruch hinterlässt keine halbe Config. Praktisch heiĂt das: Wenn etwas schiefgeht, einfach nochmal starten â kein manuelles Aufräumen nĂśtig.
Unattended auf dem Server
Wenn du nicht interaktiv sein kannst, Token vorher ins Environment:
export CRAZYROUTER_TOKEN="dein-token"
curl -fsSL https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/configure.sh | bash
(Bare-Metal-Server ohne Claude Code? Auch hier configure â setup.)
Ein Detail, das dieses Skript richtig macht: curl | bash läuft Ăźblicherweise in einer Non-Login-Shell, und die kennt die npm-Global-Pfade oft nicht. Das Skript sucht deshalb aktiv in /usr/local/bin, ~/.local/bin und ~/.npm-global/bin nach claude. Und selbst wenn es nichts findet, schreibt es die Config trotzdem fertig und gibt Diagnose-Output aus â statt auf halber Strecke zu sterben.
Noch ein Wort zum Windows-setup
Weil das der Teil ist, der auf älteren Maschinen entscheidet, ob es klappt: Das Skript versucht zuerst winget. Ist kein winget da, fällt es auf Direct Download zurĂźck (Node.js Ăźber einen fixierten LTS-Installer, Git Ăźber das offizielle Release). Und fĂźr PowerShell 5.1 aktiviert es explizit TLS 1.2 und 1.3 â ohne das scheitern die Downloads auf unpatchten Systemen mit einer Fehlermeldung, die Ăźberhaupt nicht nach TLS aussieht.
3ď¸âŁ Nachsehen, was tatsächlich geschrieben wurde
Sechs Umgebungsvariablen auf User-Ebene:
ANTHROPIC_BASE_URL Gateway-Adresse (OHNE /v1)
ANTHROPIC_AUTH_TOKEN Token
ANTHROPIC_MODEL Default-Modell
CLAUDE_MODEL Default-Modell
OPENAI_API_KEY derselbe Token
OPENAI_BASE_URL Gateway-Adresse (MIT /v1)
Warum zwei Sets? Claude Code liest die Anthropic-Konvention, viele OpenAI-kompatible Tools die andere. Ein Token deckt beide Seiten ab.
Und hier kommt die Falle, die mich am meisten gekostet hat: die beiden Adressen sind nicht symmetrisch. Bei ANTHROPIC_BASE_URL gehĂśrt nur die Root-Domain rein â der Client baut /v1/messages selbst dran. Wer das /v1 manuell ergänzt, landet bei /v1/v1/messages â und das ist falsch. Ein Gateway, das strikt nach Pfad routet, antwortet darauf mit 404. Darauf verlassen kann man sich aber nicht: mein eigenes habe ich nachgemessen, es liefert auf diesen Pfad weiterhin 200. Der zweite Fall ist der unangenehmere, weil auf dem Bildschirm gar kein Fehler auftaucht.
OPENAI_BASE_URL braucht das /v1 dagegen zwingend.
Das Perfide daran: In diese Falle tappen ausgerechnet die ordentlichen Leute. Zwei Felder zeigen auf denselben Server, also bringt man sie in dieselbe Form. Der Instinkt ist vĂśllig richtig â der Fall ist nur asymmetrisch. Es gibt keinen Linter dafĂźr, weil syntaktisch beide Werte einwandfrei sind. Ergebnis: Claude Code läuft, die anderen Tools kommen nicht durch â meist mit 404, aber was genau zurĂźckkommt, hängt vom Gateway ab. Oder umgekehrt. đ¤Ś
Wo die Werte landen
Unter Windows direkt als User-Umgebungsvariablen. Unter macOS/Linux in einer env-Datei im Home-Verzeichnis plus eine Load-Zeile in der Shell-Startdatei.
Nettes Detail: die Startdatei wird nach der echten Login-Shell ausgewählt, nicht danach, welche Datei existiert. Auf Servern liegt gerne mal eine ungenutzte .bashrc rum, während der Login tatsächlich Ăźber zsh läuft â wer dort reinschreibt, schreibt ins Nichts. echo $SHELL, bevor du irgendwas anfasst.
Alternative: pro Projekt
Wer das System-Environment nicht anfassen will, legt eine .claude/settings.json ins Projekt-Root:
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.crazyrouter.com",
"ANTHROPIC_AUTH_TOKEN": "sk-dein-token",
"ANTHROPIC_MODEL": "claude-opus-4-8"
}
}
Gilt nur fĂźr dieses Projekt â super, wenn verschiedene Repos Ăźber verschiedene Gateways laufen sollen. Aber: Das ist genauso Klartext wie die Umgebungsvariablen, nur deutlich leichter versehentlich zu committen. Wenn du diesen Weg gehst, sofort in die .gitignore. đ
Die drei Wege im Vergleich
| Skript | Desktop-GUI | settings.json |
|
|---|---|---|---|
| Scope | ganze Maschine | nur Desktop-App | nur dieses Projekt |
| URL-Normalisierung | ja (Trailing Slash weg) | nein | nein |
| Gut fßr | neue Maschine, Server, Wiederholbarkeit | Einzelplatz, gelegentlich ändern | mehrere Gateways parallel |
| Commit-Risiko | niedrig | niedrig | hoch â ď¸ |
4ď¸âŁ Desktop-App: das versteckte Panel
Mit der Desktop-App brauchst du die Umgebungsvariablen nicht anzufassen. Aber das Panel ist standardmäĂig nicht sichtbar. Es hängt unter dem Developer Mode.
Help â Troubleshooting â Enable Developer Mode
Danach startet die App einmal neu, und erst dann erscheint im MenĂź oben links ein Eintrag Developer:
Darunter dann Configure third-party inference:
Links Connection wählen. So sieht es bei mir konfiguriert aus:
âšď¸ Fairness-Hinweis: Auf meiner Maschine war der Developer Mode von vorher schon aktiv. Dass das Panel ohne ihn nicht sichtbar ist, habe ich aus der offiziellen Doku â selbst nachgestellt habe ich es nicht. Den MenĂźpfad gebe ich als dokumentiert weiter, nicht als reproduziert.
Feld fĂźr Feld:
-
Credential kind â
Static API key. Bedeutet: Credential-Quelle festnageln. Danach wird nicht mehr auf Login-Status oder Umgebungsvariablen zurĂźckgefallen. FĂźr das Debugging ist das wertvoll, weil es das Szenario âich dachte, ich gehe Ăźber mein Gateway, in Wahrheit bin ich auf den Default zurĂźckgefallen" komplett ausschlieĂt. Kehrseite: Wenn Requests trotz Konfiguration weiter zur alten Adresse gehen â hier zuerst schauen. -
Gateway base URL â Das GUI räumt das Format nicht auf, anders als das Skript. Kein Slash am Ende, kein selbst ergänztes
/v1, keine Query-Parameter. - Gateway API key â Nach dem EinfĂźgen aufs Auge-Icon klicken und drĂźberschauen. Aus dem Browser kopierte Keys schleppen gerne ein unsichtbares Leerzeichen mit, und der Fehler sieht danach genau wie âKey ungĂźltig" aus.
-
Gateway auth scheme â
beareroderx-api-key. Bestimmt, ob alsAuthorization: Bearer xxxoder alsx-api-key: xxxgesendet wird. - Artifact preview iframe origin â leer lassen, wenn du keinen speziellen Bedarf hast.
Reihenfolge der Buttons beachten: zuerst oben rechts Test connection, und erst wenn das durch ist, unten rechts Apply Changes. Der Test läuft auch auf noch nicht angewendeten Werten durch â âgetestet und Fenster zu" ist also eine zuverlässige Methode, nichts zu speichern. đž
Noch eine Debug-Richtung: links gibt es auĂerdem Sandbox & workspace und Egress. Die Desktop-App sandboxt Tool-Traffic. Wenn alle Felder stimmen und Test connection trotzdem scheitert, lohnt ein Blick in die Liste der erlaubten Egress-Hosts. Bei mir ist das nie aufgetreten (die aktuelle Config geht direkt durch) â ich notiere es nur als Richtung, nicht als reproduzierten Fall.
5ď¸âŁ Die vier Befehle đŻ
Eins â ist der Client da?
claude --version
Beweist: Im PATH liegt eine ausfĂźhrbare Datei. Das ist der komplette Geltungsbereich. Bei mir: 2.1.283
Zwei â ist die Runtime da?
node --version
Beweist: Node.js ist installiert. Auch schon alles. Bei mir: v24.21.0
Drei â ist die Config wirklich geschrieben? Nicht âich habe sie eingegeben", sondern âich habe sie zurĂźckgelesen".
env | grep -E 'ANTHROPIC|OPENAI'
'ANTHROPIC_BASE_URL','ANTHROPIC_AUTH_TOKEN','ANTHROPIC_MODEL','CLAUDE_MODEL','OPENAI_API_KEY','OPENAI_BASE_URL' |
ForEach-Object { "{0,-22} {1}" -f $_, [Environment]::GetEnvironmentVariable($_,'User') }
Zwei Dinge prĂźfen: alle sechs vorhanden, und /v1 nur bei OPENAI_BASE_URL.
Vier â Endpoint, Key und Modell gleichzeitig âŹ
ď¸ der eigentliche Test
curl -s https://api.crazyrouter.com/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-4-8",
"max_tokens": 16,
"messages": [{"role": "user", "content": "reply with: pong"}]
}'
max_tokens: 16 ist Absicht: Der Inhalt der Antwort ist irrelevant, relevant ist nur, dass es eine gibt. Eine erfolgreiche Antwort etabliert drei Fakten gleichzeitig â Endpoint erreichbar, Key akzeptiert, Modell antwortet.
Warum die ersten drei genau dort blind sind, wo es zählt
Alle drei inspizieren den Zustand deiner lokalen Maschine. Keiner von ihnen redet mit dem Gateway.
Diese Failure Modes sind fĂźr sie also komplett unsichtbar:
- đ Key abgelaufen
- đ Quota leer
- đˇď¸ Modell-ID wird vom Gateway nicht erkannt
- đ Proxy oder VPN routet die Domain woandershin
- đ§ Gateway-Domain nicht in der Egress-Whitelist
Alles davon geht durch Befehl 1â3 ohne eine einzige Warnung und fällt erst bei Nummer 4 um.
Dass die ersten drei durchgehen und der vierte scheitert, ist also nicht der Randfall â es ist die Normalform dieses Fehlers. Drei Viertel der Ăźblichen PrĂźfung sind strukturell unfähig, das zu finden, was am wahrscheinlichsten kaputt ist. Wenn du nur einen Befehl machst: mach den vierten.
Zum Auth-Header noch: ich habe mit beiden Varianten je einen echten Request geschickt. Auf diesem Gateway antworten bearer und x-api-key mit 200. Das ist aber keine allgemeine Regel â die meisten Gateways akzeptieren nur eines, und die Fehlermeldung bei der falschen Wahl ist von âKey kaputt" praktisch nicht zu unterscheiden. Doku des Anbieters lesen.
đ§° Fehlermeldungen, die ich gesehen habe (und was sie wirklich bedeuten)
âcommand not found" direkt nach erfolgreicher Konfiguration
Umgebungsvariablen greifen nur fĂźr neu gestartete Prozesse. Im alten Fenster zu prĂźfen scheitert immer, und das ist das erwartete Ergebnis, keine Fehlkonfiguration. Fenster komplett schlieĂen; ein neuer Tab reicht unter Windows nicht. Die erste Next-Step-Zeile des Skripts ist Ăźbrigens genau Open a NEW PowerShell window â geht in der englischen Ausgabe nur leicht unter.
âno token provided" â obwohl der Token gesetzt ist
Das hat mich am meisten Zeit gekostet, und ich war schon dabei, einen neuen Key auszustellen. Der Key war vĂśllig in Ordnung. Die PrĂźfung lief in einem Subprozess, und in dessen Environment war $env:ANTHROPIC_AUTH_TOKEN einfach leer.
Aus Serversicht sind die zwei Fälle nicht unterscheidbar: Es kam ein Request ohne Key an. Woher soll der Server wissen, ob du keinen hast oder ob er unterwegs verloren ging? Die Meldung war präzise und schlimmer als nutzlos, weil sie mit voller Ăberzeugung auf die falsche Hypothese zeigte.
In PowerShell lieber explizit aus der User-Scope lesen statt auf $env: zu vertrauen:
[Environment]::GetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN','User')
404, obwohl die URL richtig aussieht
/v1 doppelt. Siehe oben. Das ist die häufigste Ausprägung der Asymmetrie.
Validierungsfehler bei der Endpoint-Adresse
Aus einem Share-Link kopiert? Dann hängt am Ende vermutlich ein ?utm_source=.... Im Browser egal, als API-Endpoint bestenfalls ignoriert, schlimmstenfalls Validierungsfehler.
Git-Installation scheitert â alles hinwerfen?
Nein. Auf dieser Maschine ist Git ßberhaupt nicht installiert, trotzdem läuft claude und trotzdem geht Befehl vier durch. Git ist keine Runtime-Dependency von Claude Code. Das Full-Setup-Skript installiert es fßr die Arbeit danach, nicht als Voraussetzung.
Und weil das die zweite Stelle war, an der ich mir selbst reingefallen bin: Ich hatte âkein Git" kurzzeitig als Ursache meines Problems abgehakt und angefangen, von dieser Annahme aus zu debuggen. Genau derselbe Fehler wie mit der Versionsnummer â ich habe einen Zustand erschlossen, den ich nicht beobachtet hatte, und bin dann selbstsicher weitergelaufen.
Takeaways
Die drei Dinge, die ich mir gemerkt habe:
-
ANTHROPIC_BASE_URLohne/v1,OPENAI_BASE_URLmit. Vereinheitlichen ist falsch â und gibt nicht zwangsläufig einen 404. - Nach der Konfiguration ein neues Terminal-Fenster.
- Endpoint-Adressen mĂźssen sauber sein â keine Query-Parameter aus kopierten Links.
Und das Ăźbergeordnete: Die Disziplin liegt nicht in den Checks, sondern in der Bereitschaft, laut zu sagen, was ein Signal eigentlich beweist. Nicht worauf es hindeutet, sondern was es etabliert. Eine Versionsnummer etabliert, dass eine Datei auf der Platte liegt. Ein grĂźnes Dashboard etabliert, dass ein Check gelaufen ist und unter einem Threshold blieb. GrĂźne Tests etablieren, dass die Assertions erfĂźllt sind, die du geschrieben hast.
Keins davon heiĂt âläuft". Alle sind deutlich billiger zu bekommen als âläuft" â deshalb sammeln sie sich an, und deshalb lassen wir sie das Ergebnis ersetzen.
Und zur Sicherheit: Token nicht ins Git committen, nicht in Screenshots reinrutschen lassen, beim Maschinenwechsel den alten Key widerrufen. Klingt banal â mir ist aber genau während dieses Debuggings aufgefallen, wie oft mein Key einfach im Klartext auf meinem eigenen Bildschirm lag. đ
Und bei euch? đŹ
Mich interessiert vor allem eines: welcher der vier Befehle ist bei euch zuletzt gescheitert?
Ich habe den Verdacht, dass Nummer vier bei fast allen der Stolperstein ist â aber aus unterschiedlichen GrĂźnden. Falsches Auth-Scheme? Proxy dazwischen? Modell-ID, die das Gateway nicht kennt? Schreibt es in die Kommentare, ich ergänze gern eine Fehlerliste.





Top comments (0)