{"record":{"id":"3a8b58d666fcfe58","repo":"moeru-ai/airi","slug":"replicate-provider-is-not-configured-missing-api","errorCode":null,"errorMessage":"Replicate provider is not configured. Missing API Key.","messagePattern":"Replicate provider is not configured\\. Missing API Key\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/widgets/providers/replicate.ts","lineNumber":55,"sourceCode":"    if (config?.replicateApiKey) {\n      this.apiKey = config.replicateApiKey\n      this.replicate = new Replicate({ auth: this.apiKey })\n    }\n    else {\n      this.apiKey = ''\n      this.replicate = null\n    }\n    if (config?.replicateDefaultModel)\n      this.defaultModel = config.replicateDefaultModel\n    if (config?.replicateAspectRatio)\n      this.aspectRatio = config.replicateAspectRatio\n    if (config?.replicateInferenceSteps)\n      this.inferenceSteps = config.replicateInferenceSteps\n  }\n\n  async generate(request: ArtistryRequest): Promise<ArtistryJob> {\n    if (!this.replicate) {\n      throw new Error('Replicate provider is not configured. Missing API Key.')\n    }\n\n    const model = (request.model || request.extra?.model || this.defaultModel) as `${string}/${string}`\n    const base64Image = request.extra?.image || ''\n\n    // 1. Start with defaults\n    const hasPromptPlaceholder = JSON.stringify(request.extra).includes('{{PROMPT}}')\n    let inputOptions: Record<string, any> = {\n      go_fast: request.extra?.go_fast ?? true,\n      aspect_ratio: request.extra?.aspect_ratio ?? this.aspectRatio,\n      output_format: request.extra?.output_format ?? 'png',\n      output_quality: request.extra?.output_quality ?? 80,\n      num_inference_steps: request.extra?.num_inference_steps ?? this.inferenceSteps,\n    }\n\n    // Default prompt injection if NO placeholder is used in overrides\n    if (request.prompt && !hasPromptPlaceholder) {\n      inputOptions.prompt = request.prompt","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/main/services/airi/widgets/providers/replicate.ts#L37-L73","documentation":"Thrown by ReplicateProvider.generate() when this.replicate is null. The constructor field is only set to a live Replicate client inside initialize() when config.replicateApiKey is a truthy string; any other value (undefined, empty string, or a missing config object) leaves it null. So the error is a configuration precondition guard, not a network failure.","triggerScenarios":"Calling generate() on a ReplicateProvider instance whose initialize() was invoked with a config that lacks replicateApiKey, or initialize() was never called. Also triggered if the user cleared the API key in settings (initialize re-run with no key resets this.replicate to null).","commonSituations":"User selected the Replicate artistry provider in settings but never entered an API key; key was saved as empty string; settings store reset; provider was instantiated but initialize() skipped during a fast-path code change.","solutions":["Open Settings > Artistry/Providers and enter a Replicate API key (from replicate.com/account/api-tokens), then save — initialize() will re-run with config.replicateApiKey set.","If calling programmatically, ensure the config passed to provider.initialize({ replicateApiKey }) contains a non-empty key string before invoking generate().","Verify the settings store actually persists replicateApiKey and that the provider manager re-initializes the provider after a key change rather than reusing the stale instance.","Guard the UI so the Replicate provider cannot be selected as active until a key is present."],"exampleFix":"// before\nprovider.generate({ prompt: 'cat' })\n\n// after\nif (!provider['replicate']) {\n  throw new Error('Configure replicateApiKey before generating')\n}\nprovider.generate({ prompt: 'cat' })","handlingStrategy":"validation","validationCode":"function isReplicateConfigured(provider: ReplicateProvider): boolean {\n  // The replicate client is private; expose an isConfigured() method on the provider\n  // and call it before generate().\n  return provider.isConfigured()\n}\n// In the provider:\n// isConfigured() { return this.replicate !== null && this.apiKey !== '' }","typeGuard":"function hasReplicateApiKey(config: unknown): config is { replicateApiKey: string } {\n  return typeof config === 'object' && config !== null\n    && typeof (config as any).replicateApiKey === 'string'\n    && (config as any).replicateApiKey.length > 0\n}","tryCatchPattern":"try {\n  await provider.generate(request)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Missing API Key')) {\n    // prompt user to configure the key, do not retry\n  } else throw e\n}","preventionTips":["Disable the Replicate provider option in the UI until an API key is saved.","Call initialize() with a validated config object after every settings save.","Expose and assert provider.isConfigured() before any generate() call."],"tags":["configuration","api-key","artistry","replicate","precondition"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}