{"record":{"id":"00da61ae1cda0351","repo":"different-ai/openwork","slug":"t-providers-api-key-required","errorCode":null,"errorMessage":"t(\"providers.api_key_required\")","messagePattern":"t\\(\"providers\\.api_key_required\"\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/react-app/domains/connections/provider-auth/store.ts","lineNumber":1632,"sourceCode":"        }\n        return { connected: false, pending: true };\n      }\n      const message = describeProviderError(error, t(\"providers.oauth_failed\"));\n      setStateField(\"providerAuthError\", message);\n      throw error instanceof Error ? error : new Error(message);\n    }\n  }\n\n  async function submitProviderApiKey(providerId: string, apiKey: string) {\n    setStateField(\"providerAuthError\", null);\n    const c = options.client();\n    if (!c) {\n      throw new Error(t(\"providers.not_connected\"));\n    }\n\n    const trimmed = apiKey.trim();\n    if (!trimmed) {\n      throw new Error(t(\"providers.api_key_required\"));\n    }\n    assertProviderAllowedByDesktopPolicy(providerId);\n\n    setStateField(\"providerAuthBusy\", true);\n    try {\n      if (providerId.trim().toLowerCase() === DESKTOP_RESTRICTION_OPENCODE_PROVIDER_ID) {\n        await ensureProjectProviderDisabledState(providerId, false);\n      }\n      await c.auth.set({ providerID: providerId, auth: { type: \"api\", key: trimmed } });\n      await refreshProviders({ dispose: true });\n      return `${t(\"status.connected\")} ${providerId}`;\n    } catch (error) {\n      const message = describeProviderError(error, t(\"providers.save_api_key_failed\"));\n      setStateField(\"providerAuthError\", message);\n      throw error instanceof Error ? error : new Error(message);\n    } finally {\n      setStateField(\"providerAuthBusy\", false);\n    }","sourceCodeStart":1614,"sourceCodeEnd":1650,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/react-app/domains/connections/provider-auth/store.ts#L1614-L1650","documentation":"submitProviderApiKey validates the apiKey argument and throws when the trimmed value is empty. A blank key can never authenticate, so the store rejects it up front with the providers.api_key_required message.","triggerScenarios":"Calling submitProviderApiKey with \"\", \"   \", or a value bound to an empty input field; pasting nothing into the key dialog and submitting.","commonSituations":"User clicks Save with an empty input before client-side validation; clipboard paste failed silently; form state reset between render and submit.","solutions":["Trim and check the key in the UI before calling: if (!apiKey.trim()) show a validation message.","Add required/minLength validation on the API key input field.","Disable the submit button until the field contains non-whitespace content.","If the key is read from env/config, verify that source actually has a value."],"exampleFix":"// before\nawait store.submitProviderApiKey(providerId, formData.apiKey);\n// after\nconst key = formData.apiKey.trim();\nif (!key) return showError(\"API key is required\");\nawait store.submitProviderApiKey(providerId, key);","handlingStrategy":"validation","validationCode":"const trimmed = apiKey.trim();\nif (!trimmed) throw new Error(\"API key required before submit\");","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  await store.submitProviderApiKey(providerId, apiKey);\n} catch (e) {\n  if (String((e as Error).message).includes(\"api_key_required\")) {\n    focusApiKeyFieldWithError();\n  }\n}","preventionTips":["Validate required fields in the form before submit","Disable submit until input has non-whitespace content","Verify clipboard paste succeeded (input change event)","Check upstream env/config sources actually contain the key"],"tags":["validation","api-key","input"],"backgroundTag":"missing-api-key","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}