Skip to content

Access to CurseForge API is forbidden despite valid CF_API_KEY #3591

Description

@Orholt

Describe the problem

I'm using the itzg/minecraft-server image with TYPE=AUTO_CURSEFORGE and providing my CurseForge API key via the CF_API_KEY environment variable. However, the server fails to start due to the following error:

[mc-image-helper] ERROR : Invalid parameter provided for 'install-curseforge' command: Access to https://api.curseforge.com is forbidden or rate-limit has been exceeded. Ensure CF_API_KEY is set to a valid API key from https://console.curseforge.com/ or allow rate-limit to reset.
I’ve double-checked that my .env file contains a valid CF_API_KEY generated from https://console.curseforge.com/.

Problem:
The API call to CurseForge fails with HTTP 403 (forbidden).
I am not exceeding the rate limit based on my usage.
Even with correct CF_API_KEY, the error persists.

Container definition

version: "3.8"

services:
  mc:
    image: itzg/minecraft-server
    tty: true
    stdin_open: true
    ports:
      - "25565:25565"
    environment:
      EULA: "true"
      TYPE: "AUTO_CURSEFORGE"
      CF_API_KEY: "${CF_API_KEY}"
      CURSEFORGE_PROJECT_ID: "better-mc-neoforge-bmc5"
      CF_SLUG: "better-mc-neoforge-bmc5"
      SERVER_NAME: "rip schody marvis"
      ONLINE_MODE: false
      MAX_MEMORY: 6G
    volumes:
      - data:/data

volumes:
  data:

Container logs

+ export HOME=/data
+ HOME=/data
++ id -u
++ id -g
++ ls -lnd /data
+ log 'Running as uid=1000 gid=1000 with /data as '\''drwxr-x--- 19 1000 1000 4096 Aug  4 17:08 /data'\'''
+ local oldState
++ shopt -po xtrace
+ oldState='set -o xtrace'
+ shopt -u -o xtrace
[init] 2025-08-04 17:50:24+00:00 Running as uid=1000 gid=1000 with /data as 'drwxr-x--- 19 1000 1000 4096 Aug  4 17:08 /data'
++ paste -d, -s /etc/image.properties
+ log 'Image info: buildtime=2025-08-04T11:51:11.973Z,version=java21,revision=a569d63cf313abd8feb0ecb60447101248cfbf12'
+ local oldState
++ shopt -po xtrace
+ oldState='set -o xtrace'
+ shopt -u -o xtrace
[init] 2025-08-04 17:50:24+00:00 Image info: buildtime=2025-08-04T11:51:11.973Z,version=java21,revision=a569d63cf313abd8feb0ecb60447101248cfbf12
+ '[' '!' -e /data/eula.txt ']'
+ isTrue false
+ case "${1,,}" in
+ return 1
+ isTrue true
+ case "${1,,}" in
+ return 0
+ set +x
+ isTrue false
+ case "${1,,}" in
+ return 1
+ isTrue false
+ case "${1,,}" in
+ return 1
+ [[ -n '' ]]
+ fixJavaPath
+ which java
+ cd /data
+ export DECLARED_TYPE=AUTO_CURSEFORGE
+ DECLARED_TYPE=AUTO_CURSEFORGE
+ export DECLARED_VERSION=LATEST
+ DECLARED_VERSION=LATEST
+ isTrue ''
+ case "${1,,}" in
+ return 1
+ isTrue false
+ case "${1,,}" in
+ return 1
+ isTrue false
+ case "${1,,}" in
+ return 1
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ : ''
+ [[ -n '' ]]
+ case "${TYPE^^}" in
+ MODPACK_PLATFORM=AUTO_CURSEFORGE
+ [[ -n AUTO_CURSEFORGE ]]
+ case "${MODPACK_PLATFORM^^}" in
+ exec /start-deployAutoCF
+ ensureRemoveAllModsOff MODPACK_PLATFORM=AUTO_CURSEFORGE
+ reason=MODPACK_PLATFORM=AUTO_CURSEFORGE
+ isTrue false
+ case "${1,,}" in
+ return 1
+ args=(--results-file="$resultsFile" --force-synchronize="$CF_FORCE_SYNCHRONIZE" --force-reinstall-modloader="$CF_FORCE_REINSTALL_MODLOADER" --overrides-skip-existing="$CF_OVERRIDES_SKIP_EXISTING")
+ setArg --modpack-page-url CF_PAGE_URL
+ arg=--modpack-page-url
+ var=CF_PAGE_URL
+ [[ -n '' ]]
+ setArg --file-id CF_FILE_ID
+ arg=--file-id
+ var=CF_FILE_ID
+ [[ -n '' ]]
+ setArg --slug CF_SLUG
+ arg=--slug
+ var=CF_SLUG
+ [[ -n better-mc-neoforge-bmc5 ]]
+ args+=("${arg}=${!var}")
+ setArg --modpack-manifest CF_MODPACK_MANIFEST
+ arg=--modpack-manifest
+ var=CF_MODPACK_MANIFEST
+ [[ -n '' ]]
+ setArg --filename-matcher CF_FILENAME_MATCHER
+ arg=--filename-matcher
+ var=CF_FILENAME_MATCHER
+ [[ -n '' ]]
+ setArg --set-level-from CF_SET_LEVEL_FROM
+ arg=--set-level-from
+ var=CF_SET_LEVEL_FROM
+ [[ -n '' ]]
+ setArg --overrides-exclusions CF_OVERRIDES_EXCLUSIONS
+ arg=--overrides-exclusions
+ var=CF_OVERRIDES_EXCLUSIONS
+ [[ -n '' ]]
+ setArg --ignore-missing-files CF_IGNORE_MISSING_FILES
+ arg=--ignore-missing-files
+ var=CF_IGNORE_MISSING_FILES
+ [[ -n '' ]]
+ setArg --api-cache-default-ttl CF_API_CACHE_DEFAULT_TTL
+ arg=--api-cache-default-ttl
+ var=CF_API_CACHE_DEFAULT_TTL
+ [[ -n '' ]]
+ setArg --exclude-mods CF_EXCLUDE_MODS
+ arg=--exclude-mods
+ var=CF_EXCLUDE_MODS
+ [[ -n '' ]]
+ setArg --force-include-mods CF_FORCE_INCLUDE_MODS
+ arg=--force-include-mods
+ var=CF_FORCE_INCLUDE_MODS
+ [[ -n '' ]]
+ setArg --exclude-include-file CF_EXCLUDE_INCLUDE_FILE
+ arg=--exclude-include-file
+ var=CF_EXCLUDE_INCLUDE_FILE
+ [[ -n /image/cf-exclude-include.json ]]
+ args+=("${arg}=${!var}")
+ setArg --downloads-repo CF_DOWNLOADS_REPO
+ arg=--downloads-repo
+ var=CF_DOWNLOADS_REPO
+ [[ -n '' ]]
+ mc-image-helper install-curseforge --results-file=/data/.install-curseforge.env --force-synchronize=false --force-reinstall-modloader=false --overrides-skip-existing=false --slug=better-mc-neoforge-bmc5 --exclude-include-file=/image/cf-exclude-include.json
[mc-image-helper] 17:50:26.473 DEBUG : Loading cache index from ./.cache/curseforge/cache-index.json
[mc-image-helper] 17:50:27.968 DEBUG : JSON FETCH: uri=https://api.curseforge.com/v1/categories?gameId=432&classesOnly=true headers=[user-agent: itzg/mc-image-helper/1.48.2 (cmd=install-curseforge), x-fetch-session: 523961e6-78a8-4c31-bf5b-3f4fb218c634, x-api-key: [redacted], accept: application/json, host: api.curseforge.com]
[mc-image-helper] 17:50:28.120 DEBUG : JSON FETCH: uri=https://api.curseforge.com/v1/mods/search?gameId=432&slug=better-mc-neoforge-bmc5&classId=4471 headers=[user-agent: itzg/mc-image-helper/1.48.2 (cmd=install-curseforge), x-fetch-session: 523961e6-78a8-4c31-bf5b-3f4fb218c634, x-api-key: [redacted], accept: application/json, host: api.curseforge.com]
[mc-image-helper] 17:50:28.315 DEBUG : Failed request details: FailedRequestException(uri=https://api.curseforge.com/v1/mods/search?gameId=432&slug=better-mc-neoforge-bmc5&classId=4471, statusCode=403, body=, headers=DefaultHttpHeaders[Content-Length: 0, Connection: keep-alive, Date: Mon, 04 Aug 2025 17:50:28 GMT, X-Cache: Error from cloudfront, Via: 1.1 abe6c7fc77ee6c935374263239ff784c.cloudfront.net (CloudFront), X-Amz-Cf-Pop: WAW51-P3, X-Amz-Cf-Id: -q4m2BXKab8m3DAuKbfTFH0nC8DjZnSk0YKlS_W_P5OZSv-azI70zQ==])
[mc-image-helper] 17:50:28.316 DEBUG : Saving cache index to ./.cache/curseforge/cache-index.json
[mc-image-helper] 17:50:28.339 ERROR : Invalid parameter provided for 'install-curseforge' command: Access to https://api.curseforge.com is forbidden or rate-limit has been exceeded. Ensure CF_API_KEY is set to a valid API key from https://console.curseforge.com/ or allow rate-limit to reset.
[mc-image-helper] 17:50:28.339 DEBUG : Invalid parameter details
me.itzg.helpers.errors.InvalidParameterException: Access to https://api.curseforge.com is forbidden or rate-limit has been exceeded. Ensure CF_API_KEY is set to a valid API key from https://console.curseforge.com/ or allow rate-limit to reset.
	at me.itzg.helpers.curseforge.CurseForgeApiClient.errorMapForbidden(CurseForgeApiClient.java:316)
	at reactor.core.publisher.Mono.lambda$onErrorMap$28(Mono.java:3848)
	at reactor.core.publisher.Mono.lambda$onErrorResume$30(Mono.java:3938)
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:180)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:241)
	at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:315)
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onError(FluxContextWrite.java:121)
	at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onError(FluxDoFinally.java:119)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:149)
	at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2096)
	at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:134)
	at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:223)
	at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:275)
	at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onComplete(FluxDoFinally.java:128)
	at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:211)
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
	at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2096)
	at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:118)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
	at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
	at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:413)
	at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:455)
	at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:509)
	at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:821)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:115)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:289)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1519)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1377)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1428)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799)
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Unknown Source)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)
		at reactor.core.publisher.Mono.block(Mono.java:1779)
		at me.itzg.helpers.curseforge.CurseForgeInstaller.installByRetrievingModpackZip(CurseForgeInstaller.java:260)
		at me.itzg.helpers.curseforge.CurseForgeInstaller.lambda$install$3(CurseForgeInstaller.java:187)
		at me.itzg.helpers.curseforge.CurseForgeInstaller.install(CurseForgeInstaller.java:233)
		at me.itzg.helpers.curseforge.CurseForgeInstaller.install(CurseForgeInstaller.java:186)
		at me.itzg.helpers.curseforge.InstallCurseForgeCommand.call(InstallCurseForgeCommand.java:227)
		at me.itzg.helpers.curseforge.InstallCurseForgeCommand.call(InstallCurseForgeCommand.java:31)
		at picocli.CommandLine.executeUserObject(CommandLine.java:2031)
		at picocli.CommandLine.access$1500(CommandLine.java:148)
		at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2469)
		at picocli.CommandLine$RunLast.handle(CommandLine.java:2461)
		at picocli.CommandLine$RunLast.handle(CommandLine.java:2423)
		at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
		at picocli.CommandLine$RunLast.execute(CommandLine.java:2425)
		at picocli.CommandLine.execute(CommandLine.java:2174)
		at me.itzg.helpers.McImageHelper.main(McImageHelper.java:180)
Caused by: me.itzg.helpers.http.FailedRequestException: HTTP request of https://api.curseforge.com/v1/mods/search?gameId=432&slug=better-mc-neoforge-bmc5&classId=4471 failed with 403 Forbidden: Fetching object content
	at me.itzg.helpers.http.FetchBuilderBase.lambda$failedRequestMono$2(FetchBuilderBase.java:230)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132)
	... 50 common frames omitted
+ logError 'Failed to auto-install CurseForge modpack'
+ isDebugging
+ isTrue true
+ case "${1,,}" in
+ return 0
+ set +x
[init] 2025-08-04 17:50:28+00:00 [ERROR] Failed to auto-install CurseForge modpack 
+ exit 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussionstatus/staleNo recently activity has been seen and will be closed soon.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions