# Troubleshooting

> Source-backed failure modes: missing config, 401/400/405/500/502 responses, directory upload rejection, proxy delete without BLOB_STORE_URL, and upstream fetch errors.

- Repository: MaxLeiter/vcup
- GitHub: https://github.com/MaxLeiter/vcup
- Human docs: https://grok-wiki.com/public/docs/maxleiter-vcup-05c16ae77ebb
- Complete Markdown: https://grok-wiki.com/public/docs/maxleiter-vcup-05c16ae77ebb/llms-full.txt

## Source Files

- `cli.ts`
- `api/upload.ts`
- `api/delete.ts`
- `api/f.ts`
- `.env.example`

---

---
title: "Troubleshooting"
description: "Source-backed failure modes: missing config, 401/400/405/500/502 responses, directory upload rejection, proxy delete without BLOB_STORE_URL, and upstream fetch errors."
---

vcup surfaces failures at three boundaries: the Bun CLI (`cli.ts`) before any HTTP call, authenticated API handlers (`api/upload.ts`, `api/delete.ts`), and the public proxy (`api/f.ts`, rewritten from `/f/:slug*`). Most server errors return a plain-text body with a fixed message; the CLI prints `Upload failed:` or `Delete failed:` plus the status and response body, then exits with code `1`.

## Quick diagnosis

| Symptom | Likely layer | First check |
| --- | --- | --- |
| `Missing config` on any command | CLI | Both `VCUP_API_URL` and `VCUP_TOKEN`, or valid `~/.vcuprc` |
| `Upload failed: 401` / `Delete failed: 401` | API auth | Server `VCUP_TOKEN` matches client token; server var is set |
| `Upload failed: 400` with `Missing x-filename` | Upload API | CLI always sends `X-Filename`; custom clients must too |
| `Delete failed: 400` with `Missing x-blob-url` | Delete API | CLI sends `X-Blob-Url` with raw or resolved blob URL |
| Browser `/f/...` shows `BLOB_STORE_URL not configured` | Proxy | Deploy env `BLOB_STORE_URL` (public store URL) |
| Browser `/f/...` shows `Failed to fetch file` | Proxy upstream | Network/blob URL; store URL + slug correctness |
| `Cannot resolve proxy URL without BLOB_STORE_URL` | CLI delete | Set client `BLOB_STORE_URL` or pass raw blob URL to `vcup rm` |
| `Cannot upload a directory` | CLI | Pass a file path, not a folder |
| `File not found` | CLI | Path exists and is a file |

```text
  vcup CLI                    API (auth)                 GET /f (public)
  ---------                   ----------                 ---------------
  loadConfig()        -->     upload/delete 401/400/405
  file/dir checks     -->     (no auth on /f)
  BLOB_STORE_URL rm   -->                              f.ts 400/405/500/502
```

## CLI failures (before or after HTTP)

### Missing or incomplete config

`loadConfig()` returns immediately only when **both** `VCUP_API_URL` and `VCUP_TOKEN` are set in the environment. Otherwise it reads `~/.vcuprc` and merges: `url` from env or `rc.url`, `token` from env or `rc.token`. If the rc file is absent and the env pair is incomplete, the CLI prints a JSON example and exits:

```text
Missing config. Set VCUP_API_URL and VCUP_TOKEN env vars, or create ~/.vcuprc:
{
  "url": "https://your-vcup.vercel.app",
  "token": "your-token"
}
```

<Warning>
Setting only one of `VCUP_API_URL` or `VCUP_TOKEN` while relying on `~/.vcuprc` for the other works only when the rc file exists. With no rc file, a single env var still triggers `Missing config`.
</Warning>

Malformed `~/.vcuprc` JSON is not caught; `JSON.parse` throws and aborts the process.

### File path and stdin

| Message | Cause | Fix |
| --- | --- | --- |
| `File not found: <path>` | Resolved path does not exist | Check path; use absolute path if needed |
| `Cannot upload a directory: <path>` | `statSync` reports a directory | Upload a single file or pipe via stdin |
| Help text, exit `1` | No file args and stdin is a TTY | Pass a file, pipe stdin, or use `vcup --help` |
| `Usage: vcup rm <url>` | `vcup rm` without URL | Pass proxy or raw URL |

Stdin uploads use filename `paste.txt` and the same upload path as file uploads.

### Upload and delete HTTP errors

On non-OK responses:

```text
Upload failed: <status> <response body>
Delete failed: <status> <response body>
```

The body is whatever the API returned (plain text for built-in errors). Use the status and body string to map to the tables below.

### Proxy URL delete without `BLOB_STORE_URL`

When `vcup rm` receives a URL containing `/f/`, the CLI extracts the slug and builds `${BLOB_STORE_URL}/${slug}`. Without client env `BLOB_STORE_URL`:

```text
Cannot resolve proxy URL without BLOB_STORE_URL env var.
Pass the raw blob URL instead, or set BLOB_STORE_URL.
```

<Steps>
<Step title="Delete by raw URL">
Copy the `raw` field from upload JSON (`vcup --raw` prints it) and run `vcup rm <raw-url>`.
</Step>
<Step title="Enable proxy deletes">
Set client `BLOB_STORE_URL` to the store public URL (same value as on the Vercel project), e.g. `https://abc123.public.blob.vercel-storage.com`.
</Step>
</Steps>

## API errors (upload and delete)

Both handlers reject non-matching methods with **405** and body `Method not allowed`. Authentication uses `Authorization: Bearer <token>` compared to `process.env.VCUP_TOKEN`. If `VCUP_TOKEN` is unset on the server, every request is **401** `Unauthorized`.

### POST `/api/upload`

| Status | Body | Condition |
| --- | --- | --- |
| 405 | `Method not allowed` | Not `POST` |
| 401 | `Unauthorized` | Missing/wrong Bearer token, or server `VCUP_TOKEN` unset |
| 400 | `Missing x-filename header` | No `X-Filename` header |
| 200 | JSON `{ url, raw }` | Success |

<Note>
Upload streams the request body to `@vercel/blob` `put()` with no local try/catch. Missing or invalid `BLOB_READ_WRITE_TOKEN` on the deployment typically surfaces as a platform/runtime error rather than the messages above.
</Note>

### DELETE `/api/delete`

| Status | Body | Condition |
| --- | --- | --- |
| 405 | `Method not allowed` | Not `DELETE` |
| 401 | `Unauthorized` | Same Bearer rules as upload |
| 400 | `Missing x-blob-url header` | No `X-Blob-Url` header |
| 200 | `Deleted` | Success |

The CLI sends `X-Blob-Url` with the full Vercel Blob URL. `del(url)` is not wrapped; invalid URLs or token issues may appear as non-2xx responses or runtime errors from the Blob SDK.

## Proxy errors (`GET /f/:slug`)

`vercel.json` rewrites `/f/:slug*` to `api/f`. This route is **public** (no `VCUP_TOKEN` check). It rebuilds the blob URL as `${BLOB_STORE_URL}/${slug}` and fetches it.

| Status | Body | Condition |
| --- | --- | --- |
| 405 | `Method not allowed` | Not `GET` |
| 400 | `Missing file slug` | Empty slug after stripping `/f/` prefix |
| 500 | `BLOB_STORE_URL not configured` | `process.env.BLOB_STORE_URL` unset |
| *upstream* | `Not found` | Upstream `fetch(blobUrl)` returned `!ok`; status code copied from upstream |
| 502 | `Failed to fetch file` | `fetch` threw (network/DNS/TLS, etc.) |
| 200 | Streamed body | Success; `Content-Disposition: inline` |

<Info>
For missing blobs, the handler forwards the upstream HTTP status (often 404) with body `Not found`, not a JSON error object.
</Info>

### Upstream fetch failures

**500** means the deployment never set `BLOB_STORE_URL`. Confirm the public store URL from Vercel Storage matches `.env.example` (no trailing path beyond the store root).

**502** means the proxy could not complete `fetch` to `${storeUrl}/${slug}`. Check connectivity, that the slug matches the path segment from a successful upload `raw` URL, and that `BLOB_STORE_URL` is the store base, not a single object URL.

**Upstream non-OK** (status echoed): wrong slug, deleted object, or mismatched store URL. Compare the slug in the proxy link (`/f/<slug>`) to the pathname of the `raw` URL returned at upload time.

## Environment checklist

| Variable | Where | Symptom if wrong |
| --- | --- | --- |
| `VCUP_API_URL` / `~/.vcuprc` `url` | Client | `Missing config` or requests to wrong host |
| `VCUP_TOKEN` | Client + server | 401 on upload/delete |
| `VCUP_TOKEN` | Server only unset | All upload/delete return 401 |
| `BLOB_READ_WRITE_TOKEN` | Server (Vercel Blob) | Upload/delete blob SDK failures |
| `BLOB_STORE_URL` | Server | Proxy 500; wrong upstream 404 |
| `BLOB_STORE_URL` | Client (optional) | `vcup rm` on proxy URL fails without raw URL |
| `VCUP_BASE_URL` | Server (optional) | Upload JSON `url` uses wrong host in `url` field (not a hard error) |

## Common fix patterns

<AccordionGroup>
<Accordion title="401 on upload or delete">
Set `VCUP_TOKEN` in the Vercel project to the same secret as `token` in `~/.vcuprc` or client `VCUP_TOKEN`. Redeploy after changing server env vars.
</Accordion>
<Accordion title="Proxy link 500">
Add `BLOB_STORE_URL` on the server to the blob store public URL from the dashboard Storage tab.
</Accordion>
<Accordion title="Proxy link 502 or 404">
Verify `BLOB_STORE_URL` is the store root URL. Open the `raw` URL from upload; if that fails, the object or token is wrong. If `raw` works but `/f/` does not, slug or store URL mismatch is likely.
</Accordion>
<Accordion title="vcup rm on share link fails">
Either export client `BLOB_STORE_URL` or run `vcup rm` with the `raw` blob URL from `vcup --raw` at upload time.
</Accordion>
<Accordion title="Wrong share URL host in CLI output">
Set server `VCUP_BASE_URL` to your public origin (custom domain docs) so upload JSON `url` matches where users open `/f/...`.
</Accordion>
</AccordionGroup>

## Related pages

<CardGroup>
<Card title="Configure the CLI" href="/configure-cli">
`~/.vcuprc`, env precedence, and matching tokens to your deployment.
</Card>
<Card title="Environment variables" href="/environment-variables">
Full server and client variable reference.
</Card>
<Card title="Authentication" href="/authentication">
Bearer checks, public `/f` route, and token alignment.
</Card>
<Card title="Proxy and raw URLs" href="/proxy-and-raw-urls">
When delete and proxy serving need `BLOB_STORE_URL`.
</Card>
<Card title="API reference" href="/api-reference">
Status codes, headers, and response shapes per route.
</Card>
<Card title="Deploy on Vercel" href="/deploy-vercel">
Required env vars at first deploy.
</Card>
</CardGroup>
