{"record":{"id":"957209440bb29c8e","repo":"serverless/serverless","slug":"api-gateway-custom-domain-fetch-failed","errorCode":"API_GATEWAY_CUSTOM_DOMAIN_FETCH_FAILED","errorMessage":"V1 - Unable to fetch information about '${domain.givenDomainName}':\\n${err.message}","messagePattern":"V1 - Unable to fetch information about '(.+?)':\\\\n(.+?)","errorType":"exception","errorClass":"ServerlessError","httpStatus":null,"severity":"error","filePath":"packages/serverless/lib/plugins/aws/domains/aws/api-gateway-v1-wrapper.js","lineNumber":109,"sourceCode":"  /**\n   * Get Custom Domain Info\n   * @param {DomainConfig} domain\n   * @param {boolean} silent To issue an error or not. Not by default.\n   * @returns {Promise<DomainInfo>}\n   */\n  async getCustomDomain(domain, silent = true) {\n    // Make API call\n    try {\n      const domainInfo = await this.apiGateway.send(\n        new GetDomainNameCommand({\n          domainName: domain.givenDomainName,\n        }),\n      )\n      return new DomainInfo(domainInfo)\n    } catch (err) {\n      const statusCode = err.$metadata?.httpStatusCode\n      if (!statusCode || statusCode !== 404 || !silent) {\n        throw new ServerlessError(\n          `V1 - Unable to fetch information about '${domain.givenDomainName}':\\n${err.message}`,\n          ServerlessErrorCodes.domains.API_GATEWAY_CUSTOM_DOMAIN_FETCH_FAILED,\n          { originalMessage: err.message },\n        )\n      }\n      Logging.logWarning(`V1 - '${domain.givenDomainName}' does not exist.`)\n    }\n  }\n\n  async deleteCustomDomain(domain) {\n    // Make API call\n    try {\n      await this.apiGateway.send(\n        new DeleteDomainNameCommand({\n          domainName: domain.givenDomainName,\n        }),\n      )\n    } catch (err) {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/serverless/serverless/blob/b9d7ea51c8cce57cff1207964b9b71123673081f/packages/serverless/lib/plugins/aws/domains/aws/api-gateway-v1-wrapper.js#L91-L127","documentation":"Thrown by APIGatewayV1Wrapper.getCustomDomain when GetDomainNameCommand fails for any reason OTHER than a silent 404. The branch at line 108 suppresses only a 404 when silent=true; every other status (or a non-silent 404) bubbles up as this error. It is the read-path counterpart to creation failures and is used during deploy/teardown to decide whether a domain already exists.","triggerScenarios":"GetDomainNameCommand returns 403 (missing apigateway:GET), 429 throttling, 5xx service error, or a network failure with no httpStatusCode; or getCustomDomain is invoked with silent=false against a 404.","commonSituations":"Read-only deployment role lacking apigateway:GET permission; intermittent AWS throttling during a high-volume deploy; cross-account lookup where the assumed role cannot describe the domain; CI running with stale credentials that just expired.","solutions":["Grant apigateway:GET on the domain resource to the deploying role.","Retry the deploy - throttling and 5xx are typically transient; the SDK retryStrategy may already handle one round but a re-run is the safe fallback.","If the error is a silent 404 surfacing unexpectedly, audit call sites passing silent=false and pass silent=true where absence is expected.","Refresh AWS credentials (aws sts get-caller-identity) and confirm the region matches Globals.getRegion()."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Confirm read permission and reachability before the lookup\nasync function canReadDomain(iam, accountId, region) {\n  const policy = await iam.simulatePrincipalPolicy({\n    PolicySourceArn: `arn:aws:iam::${accountId}:role/deploys`,\n    ActionNames: ['apigateway:GET'],\n    ResourceArns: [`arn:aws:apigateway:${region}::/domainnames/*`],\n  })\n  return policy.EvaluationResults?.[0]?.EvalDecision === 'allowed'\n}","typeGuard":null,"tryCatchPattern":"try {\n  const info = await wrapper.getCustomDomain(domain, true)\n  // info is undefined when the domain was a silent 404\n} catch (err) {\n  if (err.code === 'API_GATEWAY_CUSTOM_DOMAIN_FETCH_FAILED') {\n    const status = err.cause?.$metadata?.httpStatusCode\n    if (status === 403) throw new Error('deploy role lacks apigateway:GET')\n    if (status === 429 || status >= 500) { /* retry with backoff */ }\n    else throw err\n  } else throw err\n}","preventionTips":["Pass silent=true when absence is an expected state.","Grant apigateway:GET scoped to /domainnames/* on the deploy role.","Refresh STS credentials before long-running CI deploys.","Confirm the deployment region matches the domain's region."],"tags":["aws","api-gateway","custom-domain","iam","throttling"],"backgroundTag":null,"analyzedSha":"b9d7ea51c8cce57cff1207964b9b71123673081f","analyzedAt":"2026-08-13T04:14:40.386Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}