{"record":{"id":"802e3435af7749de","repo":"PrefectHQ/fastmcp","slug":"the-api-key-is-empty","errorCode":null,"errorMessage":"The API key is empty","messagePattern":"The API key is empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/cli/deploy/credentials.py","lineNumber":47,"sourceCode":"\nCredentialSource = Literal[\"environment\", \"stored\", \"interactive\"]\n\n\nclass AuthenticationRequiredError(RuntimeError):\n    \"\"\"No Horizon credential is available without interactive authorization.\"\"\"\n\n\nclass AuthState(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\", frozen=True, populate_by_name=True)\n\n    schema_version: Literal[1] = Field(alias=\"schemaVersion\")\n    api_key: SecretStr = Field(alias=\"apiKey\")\n\n    @field_validator(\"api_key\")\n    @classmethod\n    def require_nonempty_api_key(cls, value: SecretStr) -> SecretStr:\n        if not value.get_secret_value().strip():\n            raise ValueError(\"The API key is empty\")\n        return value\n\n\n@dataclass(frozen=True)\nclass ResolvedCredential:\n    api_key: SecretStr\n    source: CredentialSource\n\n\nclass CredentialStore:\n    \"\"\"Persist the active personal Horizon API key.\"\"\"\n\n    def __init__(self, state_directory: Path | None = None) -> None:\n        if state_directory is None:\n            import fastmcp\n\n            state_directory = fastmcp.settings.home / \"cli\"\n        self.path = state_directory / \"auth.json\"","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/cli/deploy/credentials.py#L29-L65","documentation":"The AuthState pydantic model validates its api_key field with require_nonempty_api_key; a SecretStr whose value is empty or whitespace-only raises ValueError('The API key is empty'). This guards against persisting unusable credentials.","triggerScenarios":"Constructing AuthState with apiKey='' or apiKey='   ', or saving such a value through CredentialStore.save, which catches ValidationError and re-raises as StateFileError.","commonSituations":"An environment variable or paste operation supplying an empty key; a truncated copy/paste; a CI secret accidentally unset but not rejected earlier.","solutions":["Supply the actual Horizon API key when saving credentials","Trim accidental whitespace and re-run the login/save step","Re-authenticate with `fastmcp deploy login` if you don't have a valid key","Check the env var or secret source that produced the empty value"],"exampleFix":"// before\nstore.save(\"\")  # The API key is empty\n// after\nstore.save(os.environ[\"HORIZON_API_KEY\"].strip())","handlingStrategy":"validation","validationCode":"def has_api_key(value: str | None) -> bool:\n    return bool(value and value.strip())","typeGuard":"def is_nonempty_secret(value: SecretStr | None) -> bool:\n    return value is not None and bool(value.get_secret_value().strip())","tryCatchPattern":"try:\n    AuthState(schemaVersion=1, apiKey=key)\nexcept ValidationError as e:\n    print(f\"Refusing to save credentials: {e}\")","preventionTips":["Strip whitespace from keys read from env/files","Fail fast on empty secrets before reaching the save step","Audit CI secret configuration so unset vars don't surface as ''"],"tags":["pydantic","validation","api-key"],"backgroundTag":"invalid-api-key","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}