What happens
With a ~/.runpod/config.toml containing only the top-level apikey key — the form runpodctl writes and reads — every flash subcommand that contacts the account fails with RunpodAPIKeyError: No RunPod API key found. runpodctl reads the same file successfully.
Versions
|
|
runpod-flash |
1.19.0 (Python 3.13) |
runpod (runpod-python) |
1.12.0 |
runpodctl |
2.9.0-c094cac |
| OS |
Linux |
Reproduce
An isolated HOME is used so the two tools can be pointed at the same one-line file, and a placeholder key so both outputs can be shown:
$ mkdir -p /tmp/fakehome/.runpod
$ printf "apikey = 'rpa_PLACEHOLDER'\n" > /tmp/fakehome/.runpod/config.toml
$ env -u RUNPOD_API_KEY HOME=/tmp/fakehome runpodctl user
{"error":"api request failed with status 401","code":"unauthorized","status":401}
$ env -u RUNPOD_API_KEY HOME=/tmp/fakehome flash app list
RunpodAPIKeyError: No RunPod API key found. Set one with:
flash login # interactive setup
or
export RUNPOD_API_KEY=<your-api-key> # environment variable
or
echo 'RUNPOD_API_KEY=<your-api-key>' >> .env
Get a key: https://docs.runpod.io/get-started/api-keys
runpodctl's 401 is the point of comparison: it read the file and transmitted the value it found. With a real key in that same file it returns the account record. flash, against the same file, reports that no key exists.
The traceback above the message ends at:
runpod_flash/core/api/runpod.py:118 in __init__
raise RunpodAPIKeyError()
Cause
The two CLIs use different key names in the same file.
runpodctl uses a top-level apikey.
flash reads creds.get("api_key") in get_api_key() — src/runpod_flash/core/credentials.py:55 at HEAD, :46 in the released 1.19.0 — where creds comes from runpod-python's get_credentials(profile="default"). That function returns None when the parsed TOML has no default table (runpod/cli/groups/config/functions.py:108-109):
if profile not in credentials:
return None
So get_credentials() returns None, get_api_key() returns None, and flash reports "No RunPod API key found" about a file it read without error.
The write path already handles this; the read path does not
src/runpod_flash/core/credentials.py:23-28 at HEAD:
runpodctl writes top-level apikey/apiurl keys into the same config.toml that runpod-python uses for its [default] profile. We must preserve those (and any other unrelated content) when updating flash's api_key, so flash login does not clobber runpodctl's credentials.
flash login therefore preserves runpodctl's entry when writing, while get_api_key() does not consider it when reading.
The error message
The three remedies offered all require obtaining the plaintext key and placing it somewhere new (an environment variable, a .env file, or an interactive browser login), in a situation where a readable credential file holding a valid key is already present. The message cannot distinguish that case from a genuinely missing file.
runpod-python already computes the distinction. check_credentials() (functions.py:65-89) returns:
~/.runpod/config.toml is missing default profile.
get_api_key() calls get_credentials() instead, which returns a bare None, so that diagnostic is discarded.
Suggestions — either would be sufficient
- In
get_api_key(), fall back to a top-level apikey when the default profile is absent. The read path would then match the write path's existing awareness of that key, and the two CLIs would interoperate from one file in either tool's format.
- Failing that, have
RunpodAPIKeyError distinguish "no credential file" from "credential file present but no [default] profile", and name flash login as the remedy for the second case. check_credentials() already returns exactly that distinction.
Documentation
https://docs.runpod.io/flash/overview — "Before you can use Flash, you need to authenticate with your Runpod account: flash login. This saves your API key securely" — does not name the file or its schema.
https://docs.runpod.io/runpodctl/install-runpodctl — documents ~/.runpod/config.toml and an apiKey field; does not mention flash.
https://docs.runpod.io/get-started/api-keys — covers console key management only.
Neither tool's documentation states that the two use different key names in the same file.
Runpod's published agent skills (runpod/runpod-plugins-official) do state that they interoperate — runpod-usage/reference/getting-started.md: "flash login — browser OAuth that saves a real API key to ~/.runpod/config.toml, which runpodctl reads too, so one login serves both"; runpod/SKILL.md: "saves a real key to ~/.runpod/config.toml (runpodctl + flash read it)". With the file in runpodctl's format, the flash direction of that claim does not hold.
Workaround
Run flash login once. set_credentials() parses with tomlkit and assigns only its own profile, so the file ends up carrying both apikey and [default].api_key and both CLIs work from it. Confirmed on this machine: after flash login, runpodctl user and flash app list both succeed against the one file.
What happens
With a
~/.runpod/config.tomlcontaining only the top-levelapikeykey — the formrunpodctlwrites and reads — everyflashsubcommand that contacts the account fails withRunpodAPIKeyError: No RunPod API key found.runpodctlreads the same file successfully.Versions
runpod-flashrunpod(runpod-python)runpodctlReproduce
An isolated
HOMEis used so the two tools can be pointed at the same one-line file, and a placeholder key so both outputs can be shown:runpodctl's401is the point of comparison: it read the file and transmitted the value it found. With a real key in that same file it returns the account record.flash, against the same file, reports that no key exists.The traceback above the message ends at:
Cause
The two CLIs use different key names in the same file.
runpodctluses a top-levelapikey.flashreadscreds.get("api_key")inget_api_key()—src/runpod_flash/core/credentials.py:55at HEAD,:46in the released 1.19.0 — wherecredscomes from runpod-python'sget_credentials(profile="default"). That function returnsNonewhen the parsed TOML has nodefaulttable (runpod/cli/groups/config/functions.py:108-109):So
get_credentials()returnsNone,get_api_key()returnsNone, andflashreports "No RunPod API key found" about a file it read without error.The write path already handles this; the read path does not
src/runpod_flash/core/credentials.py:23-28at HEAD:flash logintherefore preservesrunpodctl's entry when writing, whileget_api_key()does not consider it when reading.The error message
The three remedies offered all require obtaining the plaintext key and placing it somewhere new (an environment variable, a
.envfile, or an interactive browser login), in a situation where a readable credential file holding a valid key is already present. The message cannot distinguish that case from a genuinely missing file.runpod-python already computes the distinction.
check_credentials()(functions.py:65-89) returns:get_api_key()callsget_credentials()instead, which returns a bareNone, so that diagnostic is discarded.Suggestions — either would be sufficient
get_api_key(), fall back to a top-levelapikeywhen thedefaultprofile is absent. The read path would then match the write path's existing awareness of that key, and the two CLIs would interoperate from one file in either tool's format.RunpodAPIKeyErrordistinguish "no credential file" from "credential file present but no[default]profile", and nameflash loginas the remedy for the second case.check_credentials()already returns exactly that distinction.Documentation
https://docs.runpod.io/flash/overview— "Before you can use Flash, you need to authenticate with your Runpod account:flash login. This saves your API key securely" — does not name the file or its schema.https://docs.runpod.io/runpodctl/install-runpodctl— documents~/.runpod/config.tomland anapiKeyfield; does not mentionflash.https://docs.runpod.io/get-started/api-keys— covers console key management only.Neither tool's documentation states that the two use different key names in the same file.
Runpod's published agent skills (
runpod/runpod-plugins-official) do state that they interoperate —runpod-usage/reference/getting-started.md: "flash login— browser OAuth that saves a real API key to~/.runpod/config.toml, which runpodctl reads too, so one login serves both";runpod/SKILL.md: "saves a real key to~/.runpod/config.toml(runpodctl + flash read it)". With the file inrunpodctl's format, theflashdirection of that claim does not hold.Workaround
Run
flash loginonce.set_credentials()parses with tomlkit and assigns only its own profile, so the file ends up carrying bothapikeyand[default].api_keyand both CLIs work from it. Confirmed on this machine: afterflash login,runpodctl userandflash app listboth succeed against the one file.