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, i99dashRoot 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-appAfter 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:
- 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, butpnpm dlxresolves the latest version via the manifest endpoint first. - 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 dlxkeeps 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-apppnpm 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@latestOr, 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 loginCommand "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 --versionIf 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 devon 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
whoamishows 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-sdkor
npm error 403 Forbidden - GET https://npm.pkg.github.com/@i99dash%2fadmin-sdkRoot cause (one of):
- You're hitting public npm.
@i99dash/admin-sdkis on GitHub Packages, not npmjs.com — public npm doesn't know about it. - You're hitting GitHub Packages but with no token (or a token without
read:packages). - 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-sdkIf 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.
Every invoke() returns {success: false, error: { code: 'user_consent_missing' }}
Symptom: calls reach the host but every envelope is user_consent_missing, regardless of which template.
Root cause (one of):
- Your
manifest.jsondoesn't list thecmdExec.*permission the template requires. The user never saw a consent prompt at install, so the host has no consent record. - The user explicitly denied that permission at install.
- Your dev cert doesn't grant the permission (cert revocation, or never granted).
Fix:
- Confirm
manifest.jsonhas 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
--provenancefrom 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=trueThis 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.