When an AI Agent fails, developers often start by changing the prompt or model. That is the wrong layer if Python receives 401 Unauthorized, Node.js throws ECONNRESET, and Windows PowerShell reports a certificate validation error for the same endpoint.
This article addresses one concrete, verifiable problem: prove the DNS, TCP, TLS, proxy, and HTTP path before sending the Agent's first model request. The preflight deliberately accepts any HTTP response as evidence that an HTTP-speaking hop responded. Only the second request evaluates the API key, model, and Agent response.
Disclosure: VectorNode is the gateway used in the examples. The network-preflight method is application-side and applies to other OpenAI-compatible endpoints.
1. The developer problem
Imagine a team moving the same Agent between a laptop, a CI runner, and a Windows workstation. The application code is unchanged, but the failures look unrelated:
Python -> 401 Unauthorized
JavaScript -> TypeError: fetch failed / ECONNRESET
PowerShell -> The remote certificate is invalid
These results are actually useful evidence. A 401 means the request reached an HTTP-speaking server or proxy. A socket reset suggests a network device, proxy, or TLS boundary closed the connection before an HTTP response existed. A certificate exception means the client rejected the peer before an HTTP status existed.
Without a preflight, the team may rotate a valid API key to fix a problem that never reached authentication, or disable certificate verification to make a local test appear green. Both actions hide the real boundary.
2. Why the same endpoint behaves differently
The official OpenAI Chat Completions reference describes a request body containing model and messages, and notes that parameter support can vary by model. For the gateway's current path and parameter details, check https://o1kqetbjmk.apifox.cn.
Those API fields are only the final layer of a longer path:
- DNS resolves the hostname to an address.
- TCP establishes a connection to port 443.
- TLS validates the certificate and negotiates encryption.
- Proxy policy may require authentication or a custom root certificate.
- HTTP carries the request and returns a status.
- API handling checks the key, model, parameters, and prompt.
Python requests, Node's native fetch, and PowerShell do not automatically inherit identical proxy and certificate behavior. Python may use REQUESTS_CA_BUNDLE; Node may need NODE_EXTRA_CA_CERTS or an approved Undici proxy dispatcher; PowerShell normally uses the Windows certificate store. A successful test in one runtime does not configure the other two.
3. Configure the endpoint without weakening trust
Use the same environment contract for the real API call:
API_KEY=replace-with-a-real-key-locally
BASE_URL=https://www.vectronode.com/v1
MODEL=replace-with-a-model-id-visible-in-your-account
The preflight sends a GET to BASE_URL without a request body. The endpoint may answer 401, 404, or another status because the path is not the Chat Completions operation. That is acceptable: any HTTP response proves that DNS, TCP, TLS, and HTTP completed far enough to receive a response.
For a private corporate CA, install the approved root certificate through the operating system or runtime trust mechanism. Do not paste a private key into a script. Do not use these shortcuts:
requests.get(..., verify=False)
NODE_TLS_REJECT_UNAUTHORIZED=0
ServerCertificateCustomValidationCallback = { $true }
Those settings remove the security property being tested. If a proxy is required, configure it explicitly through your organization's approved egress path. Node's native fetch does not automatically provide the same proxy behavior as Python or Windows PowerShell.
Keep the API key in .env for local Python and JavaScript development, add .env to .gitignore, and use a process-scoped or managed secret for PowerShell and CI.
Install the small client dependencies:
py -m venv .venv
.\.venv\Scripts\python.exe -m pip install requests python-dotenv
npm install dotenv
4. Minimal runnable preflight and Agent calls
Each example uses two clearly separated requests:
- A no-body network preflight that records an HTTP status or a transport exception.
- A Chat Completions request that sends the first Agent probe only after preflight succeeds.
The probe asks for exactly AGENT_READY:v1. This is an application check, not a claim that every model will obey every instruction. The result records the selected and reported model so a model switch can be compared without changing the network diagnosis.
Python
Save as network_probe.py:
import os
import requests
from dotenv import load_dotenv
load_dotenv()
required = ("API_KEY", "BASE_URL", "MODEL")
missing = [name for name in required if not os.getenv(name)]
if missing:
raise RuntimeError(f"Missing environment variables: {', '.join(missing)}")
base_url = os.environ["BASE_URL"].rstrip("/")
try:
preflight = requests.get(
base_url,
allow_redirects=False,
timeout=10,
)
print(f"preflight_http={preflight.status_code}")
except requests.exceptions.SSLError as exc:
raise SystemExit(f"preflight_tls_error={exc.__class__.__name__}")
except requests.exceptions.RequestException as exc:
raise SystemExit(f"preflight_transport_error={exc.__class__.__name__}")
payload = {
"model": os.environ["MODEL"],
"messages": [
{
"role": "system",
"content": "You are an Agent startup probe. Return exactly AGENT_READY:v1.",
},
{"role": "user", "content": "Run the first Agent probe."},
],
}
try:
response = requests.post(
f"{base_url}/chat/completions",
headers={
"Authorization": f"Bearer {os.environ['API_KEY']}",
"Content-Type": "application/json",
},
json=payload,
timeout=60,
)
except requests.exceptions.RequestException as exc:
raise SystemExit(f"api_transport_error={exc.__class__.__name__}")
request_id = response.headers.get("x-request-id", "unavailable")
if not response.ok:
raise SystemExit(f"api_http={response.status_code}; request_id={request_id}")
data = response.json()
choice = data["choices"][0]
content = choice["message"].get("content")
text = content.strip() if isinstance(content, str) else ""
result = {
"ok": text == "AGENT_READY:v1",
"preflight_http": preflight.status_code,
"requested_model": os.environ["MODEL"],
"reported_model": data.get("model"),
"finish_reason": choice.get("finish_reason"),
"request_id": request_id,
"text": text,
}
print(result)
if not result["ok"]:
raise SystemExit("Agent startup contract failed.")
Run it with the virtual-environment interpreter:
.\.venv\Scripts\python.exe .\network_probe.py
JavaScript
Save as network_probe.mjs. This uses the native fetch in Node.js 18 or later.
import "dotenv/config";
const required = ["API_KEY", "BASE_URL", "MODEL"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length) {
throw new Error(`Missing environment variables: ${missing.join(", ")}`);
}
const baseUrl = process.env.BASE_URL.replace(/\/$/, "");
let preflight;
try {
preflight = await fetch(baseUrl, {
method: "GET",
redirect: "manual",
});
console.log(`preflight_http=${preflight.status}`);
} catch (error) {
throw new Error(`preflight_transport_error=${error.name}`);
}
const payload = {
model: process.env.MODEL,
messages: [
{
role: "system",
content: "You are an Agent startup probe. Return exactly AGENT_READY:v1.",
},
{ role: "user", content: "Run the first Agent probe." },
],
};
let response;
try {
response = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
} catch (error) {
throw new Error(`api_transport_error=${error.name}`);
}
const requestId = response.headers.get("x-request-id") ?? "unavailable";
if (!response.ok) {
throw new Error(`api_http=${response.status}; request_id=${requestId}`);
}
const data = await response.json();
const choice = data.choices?.[0];
const content = choice?.message?.content;
const text = typeof content === "string" ? content.trim() : "";
const result = {
ok: text === "AGENT_READY:v1",
preflight_http: preflight.status,
requested_model: process.env.MODEL,
reported_model: data.model ?? null,
finish_reason: choice?.finish_reason ?? null,
request_id: requestId,
text,
};
console.log(JSON.stringify(result, null, 2));
if (!result.ok) process.exitCode = 1;
Run it from the directory containing .env:
node .\network_probe.mjs
PowerShell
This script treats an HTTP response from the preflight as network evidence, even when the response is an expected 401 or 404. It does not disable Windows certificate validation.
$required = "API_KEY", "BASE_URL", "MODEL"
$missing = @(
$required | Where-Object {
-not [Environment]::GetEnvironmentVariable($_, "Process")
}
)
if ($missing.Count -gt 0) {
throw "Missing environment variables: $($missing -join ', ')"
}
$baseUrl = $env:BASE_URL.TrimEnd('/')
try {
try {
$preflight = Invoke-WebRequest `
-Uri $baseUrl `
-Method Get `
-MaximumRedirection 0 `
-TimeoutSec 10
$preflightStatus = [int]$preflight.StatusCode
} catch {
if ($_.Exception.Response) {
$preflightStatus = [int]$_.Exception.Response.StatusCode
} else {
throw
}
}
Write-Host "preflight_http=$preflightStatus"
} catch {
throw "preflight_transport_or_tls_error=$($_.Exception.GetType().Name)"
}
$headers = @{ Authorization = "Bearer $env:API_KEY" }
$payload = [ordered]@{
model = $env:MODEL
messages = @(
[ordered]@{
role = "system"
content = "You are an Agent startup probe. Return exactly AGENT_READY:v1."
}
[ordered]@{
role = "user"
content = "Run the first Agent probe."
}
)
}
$json = $payload | ConvertTo-Json -Depth 6 -Compress
try {
$response = Invoke-RestMethod `
-Uri "$baseUrl/chat/completions" `
-Method Post `
-Headers $headers `
-ContentType "application/json" `
-Body $json `
-TimeoutSec 60
} catch {
if ($_.Exception.Response) {
$status = [int]$_.Exception.Response.StatusCode
throw "api_http=$status"
}
throw "api_transport_error=$($_.Exception.GetType().Name)"
}
$choice = $response.choices[0]
$text = ([string]$choice.message.content).Trim()
$result = [ordered]@{
ok = ($text -eq "AGENT_READY:v1")
preflight_http = $preflightStatus
requested_model = $env:MODEL
reported_model = $response.model
finish_reason = $choice.finish_reason
text = $text
}
$result | ConvertTo-Json
if (-not $result.ok) {
exit 1
}
Run it from the configured PowerShell process:
.\network_probe.ps1
5. Verify the path before switching models
A healthy diagnostic output may look like this:
{
"ok": true,
"preflight_http": 404,
"requested_model": "model-id-from-your-account",
"reported_model": "model-id-from-your-account",
"finish_reason": "stop",
"text": "AGENT_READY:v1"
}
The 404 in this example is not a failure of the preflight. It means a reachable HTTP layer answered the GET request at the configured base path; a proxy could have generated that response. The real API request is a separate POST /chat/completions call and must be judged by its own status and response shape.
Use the following decision order:
- No HTTP status: investigate DNS, TCP, TLS, proxy, or local firewall settings.
- An HTTP status but no API response: investigate authentication, path, method, or request parameters.
- HTTP success with
ok: false: inspect the model response contract; do not call the network layer broken. - Only after the first model passes should you change
MODELand repeat the same test.
For a model comparison, change one variable:
$env:MODEL = "first-current-model-id"
node .\network_probe.mjs
$env:MODEL = "second-current-model-id"
node .\network_probe.mjs
Keep the endpoint, key, prompt, parameters, and runtime trust settings unchanged. A model switch should not also become a proxy or certificate experiment.
6. Common errors and the correct layer
| Symptom | Layer | What to check |
|---|---|---|
| DNS name not resolved | DNS | Hostname spelling, resolver policy, and split-horizon DNS |
| Connection refused or reset | TCP, firewall, or proxy | Port 443 access, egress rules, proxy route, and local security software |
| Certificate verify failed | TLS trust | Approved CA installation, hostname match, system clock, and NODE_EXTRA_CA_CERTS or REQUESTS_CA_BUNDLE where appropriate |
HTTP 407
|
Proxy authentication | Proxy credentials and the approved proxy configuration |
Preflight 401 or 404
|
Network path reached | Continue to the authenticated Chat Completions request and diagnose it separately |
API 401 or 403
|
Authentication or permission | Key presence, account scope, and whether the key was revoked |
API 404
|
URL or endpoint path | One /v1 segment and the correct operation path |
model_not_found |
Model selection | Use a model ID currently visible to the account |
API 429
|
Rate or quota | Check limits and account status; do not add an unbounded retry loop |
HTTP 200 but ok: false
|
Response contract | Inspect choices, message.content, and finish_reason without logging secrets |
Do not rotate a key to fix a TLS error, and do not turn off certificate verification to fix a proxy policy. Those actions change security state without proving that the Agent can reach the intended API.
7. Security boundaries and when this pattern fits
Certificate validation is part of authentication. verify=False, NODE_TLS_REJECT_UNAUTHORIZED=0, and permissive PowerShell callbacks are not troubleshooting solutions; they allow an untrusted endpoint to impersonate the API.
Use organization-approved CA distribution and proxy configuration instead. Keep API keys in a local ignored .env, process-scoped variables, or a production secret manager. Never place a complete key in browser JavaScript, mobile code, source control, screenshots, logs, or public issues. If it is exposed, revoke and replace it.
The preflight is useful when the same Agent must run in different runtimes, networks, or CI environments. It is not a latency benchmark, an API availability guarantee, or a substitute for model-specific evaluation. It also does not prove that a proxy preserves streaming, tool calls, images, or large request bodies; test those capabilities separately.
The practical boundary is simple: first prove that the request can reach an HTTP server securely, then debug API semantics, and only then evaluate the model response.
Try the example
After creating an account and selecting a current model ID, use the tested Python, JavaScript, and PowerShell examples in the VectorNode API Quickstart:
https://github.com/Vector-Compute-Engine/api-quickstart
Never publish your complete API key in source code, screenshots, videos, or public issues.
Top comments (0)