# Fix Crowdin CLI upload sources failing with 401 unauthorized

## TL;DR

The 401 means your personal access credential is missing, expired, or lacks project scope, so generate a fresh one and check the config. Verify the credential works with a direct API call before blaming the CLI. Nine times out of ten the credential in crowdin.yml is stale or was pasted with a typo.

## The error

```text
401 Unauthorized: Invalid credentials
$ crowdin upload sources
ERROR: request failed with status code 401
```

## Fix it

### Step 1: Test the credential directly against the API

```bash
curl -s -o /dev/null -w "%{http_code}" -H "your auth header https://api.crowdin.com/api/v2/user
```

Expected: 200 means the credential is good, 401 means it is bad or expired.

### Step 2: Check what the CLI config actually sends

```bash
grep -n "api_token\|project_id" crowdin.yml | head -5
```

Expected: The config points at the right project and references a credential.

### Step 3: Generate a fresh personal access credential with project scope

```bash
node -e "console.log('in Crowdin: profile settings, API, new personal access credential, grant the project scope')"
```

Expected: You have a new credential value copied once.

### Step 4: Store it in the environment and retry the upload

```bash
crowdin upload sources | tail -3
```

Expected: With CROWDIN_PERSONAL_TOKEN set in your shell to the new credential, the upload succeeds with no 401.

## When to use this

- crowdin CLI commands fail with 401 unauthorized
- Uploads worked before and suddenly stopped

## When NOT to use this

- The CLI fails with 404, the project id is wrong
- The CLI fails with 429, that is rate limiting

## Tool and version compatibility

- Crowdin CLI v3/v4, crowdin.yml config
- Personal access credentials with project scope

## Variant phrasings

### 401 only in CI

The CI secret holding the credential expired or was rotated. Update the secret, the local config is fine.

### 401 for one project but not another

The credential lacks scope on that project. Regenerate with the right project selected.

## Why it happens

The CLI sends the configured credential on every request. If it expired, was revoked, never had the project scope, or was pasted wrong into crowdin.yml or the environment, the API answers 401 before looking at anything else.

## Edge cases

- Credentials shown once at creation, if you lost it you must regenerate
- Organization SSO can invalidate personal credentials, check with your admin
- The CLI also reads CROWDIN_PROJECT_ID from env, a mismatch 404s instead

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_-HYheu1kKoBVcxZdacQa3A
