chenhg5/cc-connect · error

read %s: %w

Error message

read %s: %w

What it means

readOAuthTokens loads Codex CLI OAuth credentials from ~/.codex/auth.json (or $CODEX_HOME/auth.json) to authenticate usage-quota queries against the ChatGPT backend. This error wraps the underlying os.ReadFile failure, preserving the file path and OS-level cause (e.g. ENOENT, EACCES). It is thrown whenever the auth.json file cannot be read, which the library treats as a hard failure because usage data cannot be fetched without a token.

Source

Thrown at agent/codex/usage.go:68

	Balance    any  `json:"balance"`
}

func (a *Agent) GetUsage(ctx context.Context) (*core.UsageReport, error) {
	tokens, err := a.readOAuthTokens(os.ReadFile)
	if err != nil {
		return nil, err
	}
	return a.fetchUsage(ctx, http.DefaultClient, tokens)
}

func (a *Agent) readOAuthTokens(readFile func(string) ([]byte, error)) (codexOAuthTokens, error) {
	path, err := codexAuthPath()
	if err != nil {
		return codexOAuthTokens{}, err
	}
	data, err := readFile(path)
	if err != nil {
		return codexOAuthTokens{}, fmt.Errorf("read %s: %w", path, err)
	}

	var payload struct {
		Tokens struct {
			AccessToken string `json:"access_token"`
			AccountID   string `json:"account_id"`
		} `json:"tokens"`
	}
	if err := json.Unmarshal(data, &payload); err != nil {
		return codexOAuthTokens{}, fmt.Errorf("parse auth.json: %w", err)
	}
	if strings.TrimSpace(payload.Tokens.AccessToken) == "" {
		return codexOAuthTokens{}, fmt.Errorf("auth.json missing tokens.access_token")
	}
	if strings.TrimSpace(payload.Tokens.AccountID) == "" {
		return codexOAuthTokens{}, fmt.Errorf("auth.json missing tokens.account_id")
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Run `codex login` to create the auth.json file
  2. Verify the resolved path: check CODEX_HOME, or confirm ~/.codex/auth.json exists (`ls -la $CODEX_HOME/auth.json` or `ls -la ~/.codex/auth.json`)
  3. If running as a daemon/service, set CODEX_HOME (or HOME) to the directory containing the logged-in auth.json
  4. Fix file permissions so the process user can read auth.json (chmod 600, correct owner)

Example fix

// before: error only visible at runtime
usage, err := agent.GetUsage(ctx)
// after: probe the auth file first and give a clear message
if _, err := os.Stat(filepath.Join(os.Getenv("CODEX_HOME"), "auth.json")); os.IsNotExist(err) {
    log.Fatal("codex auth.json not found; run `codex login` first")
}
usage, err := agent.GetUsage(ctx)
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(os.Getenv("CODEX_HOME"), "auth.json")
if os.Getenv("CODEX_HOME") == "" { p = filepath.Join(os.Getenv("HOME"), ".codex", "auth.json") }
if _, err := os.Stat(p); err != nil { return fmt.Errorf("codex auth.json not readable at %s: %w", p, err) }

Try / catch

tokens, err := readOAuthTokens()
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) {
        return fmt.Errorf("run `codex login` first (missing %s)", pe.Path)
    }
    return err
}

Prevention

When it happens

Trigger: GetUsage() -> readOAuthTokens() when readFile(path) fails: the auth.json path does not exist (Codex never logged in), the path is a directory, or the file cannot be opened due to permissions. Callers: GetUsage and the readOAuthTokens tests.

Common situations: Running usage queries on a machine where `codex login` was never executed; CODEX_HOME pointing at a nonexistent or wrong directory; running the bridge under a service account (systemd/launchd) whose HOME differs from the user that logged into Codex; restrictive file permissions after copying dotfiles.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/41652e96263a9465. Report an issue: GitHub.