koala73/worldmonitor · warning · ApiError
Expected an alphanumeric callsign of at most eight character
Error message
Expected an alphanumeric callsign of at most eight characters
What it means
When a callsign is provided to trackAircraft, it must match /^[A-Z0-9]{1,8}$/ after trimming and uppercasing — 1 to 8 alphanumeric characters. Anything with symbols, spaces, or more than 8 characters throws ApiError(400, 'Expected an alphanumeric callsign of at most eight characters').
Solutions
- Strip non-alphanumerics and uppercase, then cap at 8 characters before calling
- Use the ICAO-style callsign (e.g. 'BAW266') not the airline name or radio callsign
- If unsure, pass the icao24 instead and omit callsign
Example fix
// before
await trackAircraft(ctx, { callsign: 'DLH-441' });
// after
const callsign = raw.replace(/[^A-Z0-9]/gi, '').toUpperCase().slice(0, 8);
await trackAircraft(ctx, { callsign }); Defensive patterns
Strategy: validation
Validate before calling
if (callsign && !/^[A-Z0-9]{1,8}$/.test(callsign.trim().toUpperCase())) throw new Error('callsign must be 1-8 alphanumerics'); Type guard
const isCallsign = (v: unknown): v is string => typeof v === 'string' && /^[A-Z0-9]{1,8}$/.test(v.trim().toUpperCase()); Try / catch
try {
return await trackAircraft(ctx, { callsign });
} catch (e) {
if (e instanceof ApiError && e.status === 400) {
return { error: e.message, hint: 'use ICAO callsign like BAW266, max 8 alphanumerics' };
}
throw e;
} Prevention
- Normalize callsigns: strip symbols, uppercase, cap at 8 chars
- Use ICAO telephony designators, not marketing names
- Prefer icao24 lookups when callsign format is uncertain
When it happens
Trigger: Calling track-aircraft with callsign 'UAL1234 ', 'DLH-441', 'SPEEDBIRD9' (9 chars), or any value containing dots/dashes/slashes.
Common situations: Radio-style spoken callsigns ('Speedbird two six six') pasted verbatim; airline-prefixed strings with hyphens from external systems; duplicate suffixes from concatenated logs.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Expected a six-character hexadecimal ICAO address
- Custom scorecard bloc members must be uppercase ISO-2 codes.
- Dynamic ApiError(400, message) via local invalid() helper (e
- Dynamic ApiError(400, message) via local invalid() helper (e
- Aircraft identifier is too long
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/8aae9d39cf5f488c.
Report an issue: GitHub.
Appendix: source
Thrown at server/worldmonitor/aviation/v1/track-aircraft.ts:122
}
return 'aviation:track:all:v2';
}
// Response-level source values (TrackAircraftResponse.source):
// 'opensky' — data from OpenSky via relay
// 'wingbits' — data from Wingbits via relay
// 'none' — all real sources returned empty or failed; positions = []
export async function trackAircraft(
ctx: ServerContext,
req: TrackAircraftRequest,
): Promise<TrackAircraftResponse> {
const rawIcao24 = req.icao24 ?? '';
const rawCallsign = req.callsign ?? '';
if (rawIcao24.length > 16 || rawCallsign.length > 16) throw new ApiError(400, 'Aircraft identifier is too long', '');
const icao24 = rawIcao24.trim().toLowerCase();
const callsign = rawCallsign.trim().toUpperCase();
if (rawIcao24 && !/^[0-9a-f]{6}$/.test(icao24)) throw new ApiError(400, 'Expected a six-character hexadecimal ICAO address', '');
if (rawCallsign && !/^[A-Z0-9]{1,8}$/.test(callsign)) throw new ApiError(400, 'Expected an alphanumeric callsign of at most eight characters', '');
req = { ...req, icao24, callsign };
if (icao24 || callsign) await admitIdentifierLookup(ctx.request);
const redistributableOnly = requiresRedistributableProviders(ctx.request);
const cacheKey = `${buildCacheKey(req)}${redistributableOnly ? ':redistributable' : ''}`;
let result: { positions: PositionSample[]; source: string } | null = null;
try {
const positiveTtl = req.callsign ? CALLSIGN_CACHE_TTL : CACHE_TTL;
const negativeTtl = req.callsign ? CALLSIGN_NEGATIVE_TTL : CACHE_TTL;
result = await cachedFetchJson<{ positions: PositionSample[]; source: string }>(
cacheKey, positiveTtl, async () => {
const relayBase = getRelayBaseUrl();
const isCallsignOnly = !!req.callsign && !req.icao24 && isDegenerateBbox(req);
// For callsign-only searches, try Wingbits first — commercial flights like UAE20
// are Wingbits-exclusive and not visible in OpenSky. Trying OpenSky first wastes
// time and may return an early hit with no callsign match.View on GitHub (pinned to 7d06c8633d)