Skip to content

Prevent session creation when API keys are not configured #626

@jeremyeder

Description

@jeremyeder

Problem

Users can create sessions that immediately get stuck in PendingFailed because required secrets (ambient-runner-secrets with ANTHROPIC_API_KEY) are missing. The operator catches this during reconciliation and sets SecretsReady=False with reason RunnerSecretMissing, but by then the user has already created a broken session and is staring at a "Pending" status with no actionable guidance.

The validation happens too late — at the operator level during pod provisioning — instead of at session creation time.

Expected Behavior

  • Users cannot create a session unless the required API key is configured for the workspace
  • The "Create Session" button is disabled with a clear message when prerequisites are missing
  • The UI guides users to the Settings page to configure their API key first

Root Cause

The session creation flow has no pre-flight validation:

  1. Frontend (components/frontend/src/components/create-session-dialog.tsx) — validates form fields (model, temperature, etc.) but does NOT check whether API keys exist
  2. Backend (components/backend/handlers/sessions.go:524-730) — creates the AgenticSession CR immediately without checking for secrets
  3. Operator (components/operator/internal/handlers/sessions.go:637-687) — checks for ambient-runner-secrets during reconciliation, fails the session if missing

The integrations status endpoint (GET /api/auth/integrations/status) does not report API key configuration status, so the frontend has no way to know.

Implementation Plan

1. Backend — Add pre-flight check endpoint

File: components/backend/handlers/secrets.go

Add a new handler:

// GetRunnerSecretsStatus handles GET /api/projects/:projectName/runner-secrets/status
// Returns whether required runner secrets are configured (without exposing values).
func GetRunnerSecretsStatus(c *gin.Context) {
    projectName := c.Param("projectName")
    k8sClient, _ := GetK8sClientsForRequest(c)
    // ...
    secret, err := k8sClient.CoreV1().Secrets(projectName).Get(ctx, "ambient-runner-secrets", ...)
    
    vertexEnabled := os.Getenv("CLAUDE_CODE_USE_VERTEX") == "1"
    
    response := gin.H{
        "configured": false,
        "vertexEnabled": vertexEnabled,
    }
    
    if vertexEnabled {
        // Check for ambient-vertex secret in operator namespace instead
        response["configured"] = true // Vertex keys are cluster-level, not per-workspace
    } else if secret != nil {
        _, hasKey := secret.Data["ANTHROPIC_API_KEY"]
        response["configured"] = hasKey && len(secret.Data["ANTHROPIC_API_KEY"]) > 0
    }
    
    c.JSON(http.StatusOK, response)
}

File: components/backend/routes.go

Add route:

projectGroup.GET("/runner-secrets/status", handlers.GetRunnerSecretsStatus)

2. Frontend — Add proxy route

File: components/frontend/src/app/api/projects/[name]/runner-secrets/status/route.ts

Standard GET proxy following existing pattern.

3. Frontend — Add API function and query hook

File: components/frontend/src/services/api/secrets.ts (or wherever runner-secrets API lives)

export type RunnerSecretsStatus = {
  configured: boolean;
  vertexEnabled: boolean;
};

export async function getRunnerSecretsStatus(projectName: string): Promise<RunnerSecretsStatus> {
  return apiClient.get<RunnerSecretsStatus>(`/projects/${projectName}/runner-secrets/status`);
}

Add a useRunnerSecretsStatus(projectName) query hook.

4. Frontend — Gate session creation in dialog

File: components/frontend/src/components/create-session-dialog.tsx

  • Call useRunnerSecretsStatus(projectName)
  • If !status.configured && !status.vertexEnabled:
    • Show a warning banner at the top of the dialog: "An Anthropic API key is required to create sessions. Configure it in workspace Settings."
    • Include a link/button to navigate to the Settings page
    • Disable the "Create" submit button
  • If status.vertexEnabled, no API key warning needed (Vertex uses cluster-level credentials)

5. Backend — Optional: reject at CreateSession handler

File: components/backend/handlers/sessions.go

As defense-in-depth, add a check in CreateSession() before creating the CR:

// Check runner secrets before creating session
vertexEnabled := os.Getenv("CLAUDE_CODE_USE_VERTEX") == "1"
if !vertexEnabled {
    _, err := k8sClient.CoreV1().Secrets(projectName).Get(ctx, "ambient-runner-secrets", metav1.GetOptions{})
    if err != nil {
        c.JSON(http.StatusPreconditionFailed, gin.H{
            "error": "ANTHROPIC_API_KEY not configured. Go to workspace Settings to add it.",
            "code": "MISSING_API_KEY",
        })
        return
    }
}

This ensures the API also rejects session creation even if the frontend check is bypassed.

Files to Create/Modify

File Action
components/backend/handlers/secrets.go Add GetRunnerSecretsStatus handler
components/backend/routes.go Add GET /runner-secrets/status route
components/backend/handlers/sessions.go Add pre-creation secret check in CreateSession
components/frontend/src/app/api/projects/[name]/runner-secrets/status/route.ts Create proxy route
components/frontend/src/services/api/secrets.ts Add getRunnerSecretsStatus() function
components/frontend/src/services/queries/use-secrets.ts Add useRunnerSecretsStatus() hook
components/frontend/src/components/create-session-dialog.tsx Gate creation on API key status

Verification

  1. Without API key configured: "Create Session" button disabled, warning shown with link to Settings
  2. After configuring API key in Settings: "Create Session" button enabled, session starts normally
  3. With Vertex AI enabled: no warning, creation allowed (cluster-level credentials)
  4. Direct API call without key: returns 412 Precondition Failed with actionable error message

Metadata

Metadata

Assignees

No one assigned

    Labels

    amber:auto-fixAmber agent: automated low-risk fixes (formatting, linting)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions