i99dash docs
Troubleshooting

Troubleshooting

Issues we've actually hit, with root causes and fixes. Symptom → cause → fix.

If you hit something not here, open an issue.

Install / scaffold

ERR_PNPM_DLX_MULTIPLE_BINS on pnpm dlx @i99dash/sdk-cli

Symptom:

ERR_PNPM_DLX_MULTIPLE_BINS  Could not determine executable to run.
@i99dash/sdk-cli has multiple binaries: sdk-i99dash, i99dash

Root cause: @i99dash/sdk-cli versions ≤ 0.1.5 declared two binary entries (sdk-i99dash and i99dash). pnpm's dlx refuses to guess which to run when there are multiple.

Fix (any of these):

# 1. Upgrade — 0.1.6 ships only `sdk-i99dash`, dlx works again
pnpm dlx @i99dash/sdk-cli@latest init my-app

# 2. Pin to the multi-bin version with explicit bin selection
pnpm --package=@i99dash/sdk-cli dlx sdk-i99dash init my-app

# 3. Use npx, which doesn't have the multi-bin guard
npx -y @i99dash/sdk-cli init my-app

After install, pnpm exec sdk-i99dash <command> works in all versions.

ERR_PNPM_FETCH_404 on pnpm dlx @i99dash/sdk-cli

Symptom: immediately after a fresh release of the CLI, pnpm dlx errors:

ERR_PNPM_FETCH_404  GET https://registry.npmjs.org/@i99dash%2Fsdk-cli: Not Found - 404
@i99dash/sdk-cli is not in the npm registry, or you have no permission to fetch it.

But curl https://registry.npmjs.org/@i99dash/sdk-cli returns 200, and npm works fine.

Root cause: a known interaction between two layers:

  1. npm registry CDN propagation: after a publish, the manifest endpoint (/<scope>/<name>) takes 5–30 minutes to replicate across CDN regions. The version-specific endpoint (/<scope>/<name>/<version>) is consistent immediately, but pnpm dlx resolves the latest version via the manifest endpoint first.
  2. pnpm's resolution cache: when pnpm gets a 404 from the manifest endpoint, it caches the result for the rest of the dlx invocation. Even when the CDN catches up seconds later, pnpm dlx keeps returning the stale 404 until the cache is cleared.

Fix (any of these):

# Easiest — npx fetches the manifest directly without pnpm's cache
npx -y @i99dash/sdk-cli init my-app

# Or — clear pnpm's metadata cache + retry
pnpm store prune
pnpm dlx @i99dash/sdk-cli init my-app

# Or — pin to the exact version (still hits manifest, but clearer error)
pnpm dlx @i99dash/[email protected] init my-app

pnpm install of @i99dash/sdk-cli inside an existing project is unaffected — only pnpm dlx triggers the bug.

DNS / ENOTFOUND on first sdk-i99dash login

Symptom: login attempt fails immediately with a DNS resolution error pointing at a stale backend hostname.

Root cause: you're on @i99dash/sdk-cli ≤ 0.1.1. That version pinned a backend URL that no longer resolves; the URL was changed in 0.1.2.

Fix: upgrade.

pnpm add -D @i99dash/sdk-cli@latest

Or, until you upgrade, override per-command — ops will give you the current backend URL:

sdk-i99dash login --backend-url <URL_FROM_OPS>
# or set the env var
I99DASH_BACKEND_URL=<URL_FROM_OPS> sdk-i99dash login

Command "sdk-i99dash" not found

Symptom: pnpm exec sdk-i99dash <command> errors with ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL.

Root cause: the CLI is a devDependency, so the sdk-i99dash binary lives in node_modules/.bin/. If node_modules/ is missing (you deleted it, moved the project without it, or skipped pnpm install), the binary isn't there.

Fix:

pnpm install
pnpm exec sdk-i99dash --version

If you moved the project to a fresh directory, always re-run pnpm install — pnpm symlinks per-project.

Login

Browser opens but the verification page is 404

Symptom: sdk-i99dash login opens an OAuth device-code URL and the browser shows 404.

Root cause: the i99dash backend has enable_sdk=false. The OAuth device-code endpoints (and the entire /api/v1/mini-apps/* surface) are gated on the SDK feature flag, which is off by default.

Fix: contact your i99dash administrator to enable the SDK on the backend (this is a production-readiness checkbox, not a per-user setting).

Login hangs at "waiting for authorization…"

Symptom: device-code flow opens the browser, you authorize, but the CLI keeps polling forever.

Root cause (most common): the browser tab finished but you didn't click the "Approve" button. The backend marks the request pending until you explicitly approve.

Fix: re-open the URL printed by the CLI, click Approve. If lost, kill the CLI (Ctrl+C) and re-run sdk-i99dash login.

Publish

403 Forbidden on presigned PUT during sdk-i99dash publish

Symptom: publish succeeds at validation + build, then fails on the bundle upload step.

Root cause (one of):

  • The bucket's CORS rule doesn't include the origin you're publishing from. The dev-server runs on http://localhost:5173; if your local has shifted to a different port, CORS rejects the PUT.
  • The presigned URL expired (default 5 min from issue). Slow networks on large bundles can hit this.

Fix:

  • Restart sdk-i99dash dev on the default port (5173).
  • Re-run publish (it requests a fresh presigned URL).

publish succeeds but the catalog never shows the app

Symptom: terminal reports successful upload + register, but the admin dashboard's mini-apps list doesn't show your app.

Root cause (likely): the admin dashboard catalog page caches the manifest for a few seconds. Or your account doesn't have catalog read permission.

Fix:

  • Hard-refresh the admin page (Cmd/Ctrl + Shift + R).
  • Confirm whoami shows your account is a developer (sdk-i99dash whoami).

Backend

enable_sdk is true but oauth/device/authorize still 404s

Symptom: you flipped ENABLE_SDK=true in DO App Platform, redeployed, but the OAuth endpoints still 404.

Root cause: the enable_sdk setting is gated by a model_validator that checks all four MINIAPPS_SPACES_* env vars are non-empty before honoring the flag. If any one is missing or set to placeholder, the validator forces enable_sdk=false regardless.

Fix: confirm all four are set with real values (mint a Spaces key, paste both halves):

  • MINIAPPS_SPACES_ENDPOINT (e.g. https://fra1.digitaloceanspaces.com)
  • MINIAPPS_SPACES_BUCKET (e.g. i99dash-miniapps)
  • MINIAPPS_SPACES_ACCESS_KEY (SECRET, real value)
  • MINIAPPS_SPACES_SECRET_KEY (SECRET, real value)

The backend's prestart.sh logs enable_sdk forced to false: missing X, Y, Z when this happens — check doctl apps logs <app-id> --type=deploy for that line.

Privileged mini-apps (admin-sdk)

pnpm add @i99dash/admin-sdk returns 404 / 403

Symptom:

ERR_PNPM_FETCH_404  GET https://registry.npmjs.org/@i99dash%2Fadmin-sdk

or

npm error 403 Forbidden - GET https://npm.pkg.github.com/@i99dash%2fadmin-sdk

Root cause (one of):

  1. You're hitting public npm. @i99dash/admin-sdk is on GitHub Packages, not npmjs.com — public npm doesn't know about it.
  2. You're hitting GitHub Packages but with no token (or a token without read:packages).
  3. Your GitHub account hasn't been granted access to the package — even with a valid token, restricted packages 403 for unauthorised consumers.

Fix:

# .npmrc — at the project root
@i99dash:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
export GITHUB_TOKEN=ghp_xxx  # classic token with read:packages scope
pnpm add @i99dash/admin-sdk

If you still 403 after the .npmrc + token fix, your account isn't on the access list — request a cmdExec.* permission grant from your i99dash admin. The package is gated, not just scoped.

NotInsideHostError outside the host (Storybook, SSR, Node)

Symptom: AdminClient.fromWindow() throws NotInsideHostError: window is undefined or host bridge missing on window.

Root cause: there is no host bridge — your code is running outside the i99dash host (server-rendered, in a unit-test runner, or in Storybook).

Fix: guard the construction and render a fallback:

import { AdminClient, NotInsideHostError } from '@i99dash/admin-sdk';

let client: AdminClient | null = null;
try {
  client = AdminClient.fromWindow({ context, catalog });
} catch (err) {
  if (err instanceof NotInsideHostError) client = null;
  else throw err;
}

For tests, use AdminClient.withBridge({ bridge: new FakeAdminBridge(...) }) instead — see Privileged mini-apps — Local development.

Symptom: calls reach the host but every envelope is user_consent_missing, regardless of which template.

Root cause (one of):

  1. Your manifest.json doesn't list the cmdExec.* permission the template requires. The user never saw a consent prompt at install, so the host has no consent record.
  2. The user explicitly denied that permission at install.
  3. Your dev cert doesn't grant the permission (cert revocation, or never granted).

Fix:

  • Confirm manifest.json has the right permission:
    { "permissions": ["cmdExec.read", "cmdExec.control"] }
  • Re-validate: sdk-i99dash validate.
  • Have the user re-install the app — re-install re-prompts for consent.
  • Confirm your developer account has cmdExec.* granted (see Privileged mini-apps — Prerequisites).

UnknownTemplateError on a template you know exists server-side

Symptom: admin.invoke('pm.uninstall', ...) throws UnknownTemplateError: unknown template id "pm.uninstall" even though the migration shipped.

Root cause: your client-side catalog snapshot is stale. The SDK validates templateId against catalog: snapshotFromList(...) before hitting the bridge, so a stale snapshot blocks valid templates.

Fix: refresh the catalog the host injects on window.__i99dashAdminCatalog. In dev, update your hardcoded DEV_CATALOG constant. The host's own SQLite-cached catalog is the authoritative source for the actual dispatch — the client snapshot only drives client-side affordances and the early UnknownTemplateError guard.

session_cap_expired envelopes after a long-idle period

Symptom: privileged ops worked yesterday, today every tier-2 call returns {success: false, error: { code: 'session_cap_expired' }}.

Root cause: install-time session cap has a 30-day TTL. After expiry the host triggers a refresh on the next call — but if the refresh itself fails (offline, backend down), the next ~5–30s of calls still see the expired cap.

Fix: retry once after 500–1000ms — the refresh usually completes in that window. Persistent expiry means the user needs to re-install.

CI / GitHub Actions

422 Unprocessable Entity on npm publish with --provenance

Symptom:

npm error 422 - Error verifying sigstore provenance bundle:
Unsupported GitHub Actions source repository visibility: "private".
Only public source repositories are supported when publishing with provenance.

Root cause: npm's sigstore provenance attestation requires the source GitHub repo to be public. Publishing from a private repo with NPM_CONFIG_PROVENANCE: 'true' always fails.

Fix (pick one):

  • Make the repo public (recommended for SDK distribution — packages are public anyway).
  • Drop --provenance from the workflow. You lose the sigstore-signed link between commit and tarball, but classic-token publish keeps working.

gh workflow run doesn't actually run the publish job

Symptom: you trigger the workflow, it completes in seconds with green check, but no packages were published.

Root cause: the publish job is gated on releases_created == 'true'. When release-please runs and finds no qualifying conventional commits, it reports false, the publish job is skipped, and the workflow completes successfully without publishing.

Fix: trigger with force_publish: true (manual recovery path):

gh workflow run release-please.yml \
    --repo <owner>/sdk-i99dash \
    -f force_publish=true

This bypasses release-please and publishes the versions currently in package.json. npm rejects re-publishing existing versions with 403, which is harmless — the loop continues to the next package.

On this page