Skip to content

flash cannot read a ~/.runpod/config.toml written by runpodctl; the error suggests exporting the key instead #363

Description

@kbennett2000

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

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions