KeygraphHQ/shannon · error · Error

SHANNON_AI_MODEL must be "<provider>:<model-id>", got "${tri

Error message

SHANNON_AI_MODEL must be "<provider>:<model-id>", got "${trimmed}". Example: ${DEFAULT_MODEL_SPEC}

What it means

parseModelSpec rejects a SHANNON_AI_MODEL spec that contains no colon. The spec must follow '<provider>:<model-id>'; the FIRST colon is the separator so colons inside a model id (notably Bedrock ARNs) survive. With no separator the provider half cannot be distinguished from the model half.

Source

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

  }
  return raw;
}

export interface ModelSpec {
  providerId: string;
  modelId: string;
}

/**
 * Parse a `<provider>:<model-id>` spec. Splits on the first colon only, so colons
 * inside a model ID survive. The provider id is passed through as given — pi's
 * registry validates it later — so this throws only on a malformed spec.
 */
export function parseModelSpec(spec: string): ModelSpec {
  const trimmed = spec.trim();
  const separator = trimmed.indexOf(':');
  if (separator === -1) {
    throw new Error(
      `SHANNON_AI_MODEL must be "<provider>:<model-id>", got "${trimmed}". Example: ${DEFAULT_MODEL_SPEC}`,
    );
  }

  const providerId = trimmed.slice(0, separator).trim();
  const modelId = trimmed.slice(separator + 1).trim();

  if (!providerId || !modelId) {
    throw new Error(
      `SHANNON_AI_MODEL must be "<provider>:<model-id>", got "${trimmed}". Example: ${DEFAULT_MODEL_SPEC}`,
    );
  }

  return { providerId, modelId };
}

/** Resolve the run's model from SHANNON_AI_MODEL, falling back to the default. */
export function resolveModelSpec(): ModelSpec {

View on GitHub (pinned to 1ae0a142f8)

Solutions

  1. Use the form '<provider>:<model-id>', e.g. 'anthropic:claude-sonnet-4-6'.
  2. Look up the exact provider:model id at https://pi.dev/models.
  3. Leave SHANNON_AI_MODEL unset to use the default 'anthropic:claude-sonnet-4-6'.

Example fix

# before
export SHANNON_AI_MODEL=claude-sonnet-4-6

# after
export SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
Defensive patterns

Strategy: validation

Validate before calling

const spec = process.env.SHANNON_AI_MODEL;
if (spec && !spec.includes(':')) {
  console.error(`SHANNON_AI_MODEL="${spec}" must be "<provider>:<model-id>". Example: anthropic:claude-sonnet-4-6`);
  process.exit(1);
}

Type guard

function isModelSpec(spec: string): boolean {
  const sep = spec.indexOf(':');
  if (sep === -1) return false;
  const provider = spec.slice(0, sep).trim();
  const model = spec.slice(sep + 1).trim();
  return provider.length > 0 && model.length > 0;
}

Prevention

When it happens

Trigger: SHANNON_AI_MODEL set to a bare model id ('claude-sonnet-4-6'), a bare provider name ('anthropic'), or a slash-delimited id ('anthropic/claude-sonnet-4-6'), then parseModelSpec/resolveModelSpec runs during preflight.

Common situations: Copying only the model id from pi.dev/models and dropping the provider prefix; pasting a slash-style id from another tool; an unset $MODEL variable producing 'anthropic:' which still has a colon but see error 2.

Related errors


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