KeygraphHQ/shannon · error · Error

SHANNON_AI_OPENAI_FORMAT applies to gateway runs only. Set S

Error message

SHANNON_AI_OPENAI_FORMAT applies to gateway runs only. Set SHANNON_AI_BASE_URL, or unset the format to call OpenAI directly.

What it means

resolveGatewayFormat rejects SHANNON_AI_OPENAI_FORMAT when calling OpenAI directly with no gateway configured. The format only matters behind a custom endpoint that can serve either wire format; a direct OpenAI call uses one fixed API, so the variable is rejected rather than silently dropped.

Source

Thrown at apps/worker/src/ai/models.ts:311

 * Validate SHANNON_AI_OPENAI_FORMAT against the rest of the configuration and
 * return the format a gateway run should use.
 *
 * The variable only reaches a request when both an OpenAI model and a gateway
 * are configured, so it is rejected outside that combination rather than
 * silently ignored.
 */
export function resolveGatewayFormat(providerId: string, baseUrl: string | undefined): OpenAiFormat {
  const configured = resolveOpenAiFormat();
  if (!configured) return DEFAULT_OPENAI_FORMAT;

  if (providerId !== 'openai') {
    throw new Error(
      `SHANNON_AI_OPENAI_FORMAT applies to openai models only, but SHANNON_AI_MODEL selects "${providerId}". ` +
        `${providerId} serves a single API, so there is no format to choose.`,
    );
  }
  if (!baseUrl) {
    throw new Error(
      'SHANNON_AI_OPENAI_FORMAT applies to gateway runs only. Set SHANNON_AI_BASE_URL, or unset the format to call OpenAI directly.',
    );
  }
  return configured;
}

/**
 * Resolve SHANNON_AI_MODEL, build a ModelRuntime primed with the provider's
 * credential, and look the model up in it.
 */
export async function resolveModelSelection(): Promise<ModelSelection> {
  const { providerId, modelId } = resolveModelSpec();
  const credentials = resolveProviderCredentials(providerId);
  const format = resolveGatewayFormat(providerId, credentials.baseUrl);

  const modelRuntime = await createModelRuntime(providerId, credentials.apiKey);

  const model = resolveModel(modelRuntime, providerId, modelId, credentials.baseUrl, format);

View on GitHub (pinned to 1ae0a142f8)

Solutions

  1. Set SHANNON_AI_BASE_URL to the gateway endpoint if you route OpenAI through one.
  2. Unset SHANNON_AI_OPENAI_FORMAT to call OpenAI directly.

Example fix

# before
export SHANNON_AI_MODEL=openai:gpt-4o
export SHANNON_AI_OPENAI_FORMAT=responses
# SHANNON_AI_BASE_URL not set

# after (pick one)
export SHANNON_AI_BASE_URL=https://my-gateway.example.com   # keep the format
# OR
unset SHANNON_AI_OPENAI_FORMAT                                # direct OpenAI
Defensive patterns

Strategy: validation

Validate before calling

const provider = (process.env.SHANNON_AI_MODEL ?? 'anthropic:claude-sonnet-4-6').split(':')[0];
const formatSet = !!process.env.SHANNON_AI_OPENAI_FORMAT?.trim();
const hasGateway = !!process.env.SHANNON_AI_BASE_URL?.trim();
if (formatSet && provider === 'openai' && !hasGateway) {
  console.error('SHANNON_AI_OPENAI_FORMAT requires SHANNON_AI_BASE_URL. Set the gateway URL or unset the format.');
  process.exit(1);
}

Prevention

When it happens

Trigger: SHANNON_AI_OPENAI_FORMAT is set, SHANNON_AI_MODEL selects an openai provider, but SHANNON_AI_BASE_URL is unset, then resolveModelSelection() runs.

Common situations: Setting the format in anticipation of a proxy/gateway but forgetting SHANNON_AI_BASE_URL; a leftover format var after removing a gateway.

Related errors


AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12). Data as JSON: /api/errors/2946a6d11b6750b0. Report an issue: GitHub.