{"record":{"id":"de4fb05e50c49e92","repo":"jackwener/OpenCLI","slug":"coingecko-currency-must-look-like-a-currency-slug-de4fb0","errorCode":null,"errorMessage":"coingecko currency must look like a currency slug (got \"${args.currency}\")","messagePattern":"coingecko currency must look like a currency slug \\(got \"(.+?)\"\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/coingecko/global.js","lineNumber":21,"sourceCode":"import { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';\n\ncli({\n    site: 'coingecko',\n    name: 'global',\n    access: 'read',\n    description: 'Aggregate crypto market stats: total market cap, volume, dominance',\n    domain: 'api.coingecko.com',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'currency', type: 'string', default: 'usd', help: 'Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)' },\n    ],\n    columns: ['currency', 'totalMarketCap', 'totalVolume24h', 'marketCapChange24hPct', 'btcDominancePct', 'ethDominancePct', 'activeCryptocurrencies', 'markets', 'ongoingIcos', 'updatedAt'],\n    func: async (args) => {\n        const currency = String(args.currency ?? 'usd').trim().toLowerCase();\n        if (!/^[a-z0-9-]{2,20}$/.test(currency)) {\n            throw new ArgumentError(`coingecko currency must look like a currency slug (got \"${args.currency}\")`);\n        }\n        let resp;\n        try {\n            resp = await fetch('https://api.coingecko.com/api/v3/global', { headers: { 'User-Agent': 'Mozilla/5.0' } });\n        }\n        catch (err) {\n            throw new CommandExecutionError(`coingecko global request failed: ${err?.message ?? err}`);\n        }\n        if (resp.status === 429) {\n            throw new CommandExecutionError(\n                'coingecko returned HTTP 429 (rate limited)',\n                'Free tier allows ~30 calls/min. Wait and retry.',\n            );\n        }\n        if (!resp.ok) {\n            throw new CommandExecutionError(`coingecko global returned HTTP ${resp.status}`);\n        }\n        let body;","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coingecko/global.js#L3-L39","documentation":"The `coingecko global` command validates the `currency` argument against the slug pattern /^[a-z0-9-]{2,20}$/ after trimming and lowercasing; anything that doesn't match (too short/long, or containing spaces, symbols, or uppercase) throws this ArgumentError before any network call. The value must look like a plausible quote-currency slug (usd, cny, eur, jpy, ...).","triggerScenarios":"Passing currency values like '', 'u', 'US DOLLAR', 'us dollar', 'usd!', '$usd', or a 25+ character string — e.g. `coingecko global --currency 'us dollar'` or `func({ currency: 'USD$' })`.","commonSituations":"Users typing human-readable currency names instead of slugs, values with trailing whitespace or punctuation copied from elsewhere, uppercase currency codes pasted without normalization (the library lowercases, but embedded spaces/symbols still fail), or empty strings from unset environment variables.","solutions":["Use a lowercase currency slug of 2-20 chars: `--currency usd` (default), eur, cny, jpy, gbp.","Sanitize input first: `currency = String(raw).trim().toLowerCase().replace(/[^a-z0-9-]/g, '')` before calling.","If the input is a currency name ('US Dollar'), map it to its slug ('usd') before invoking.","Note: passing this pattern check does not guarantee CoinGecko supports the currency — an unsupported slug later raises 'coingecko has no market totals for currency'."],"exampleFix":"// before\nawait globalCmd.func({ currency: 'US Dollar' });\n// after\nconst slug = String(raw).trim().toLowerCase().replace(/\\s+/g, '-').replace(/[^a-z0-9-]/g, '');\nawait globalCmd.func({ currency: slug || 'usd' });","handlingStrategy":"validation","validationCode":"const currency = String(raw ?? 'usd').trim().toLowerCase();\nif (!/^[a-z0-9-]{2,20}$/.test(currency)) throw new Error(`unsupported currency slug: ${raw}`);","typeGuard":"function isCurrencySlug(v) { return typeof v === 'string' && /^[a-z0-9-]{2,20}$/.test(v); }","tryCatchPattern":"try {\n  await globalCmd.func({ currency });\n} catch (e) {\n  if (/must look like a currency slug/.test(e.message)) {\n    return globalCmd.func({ currency: 'usd' }); // safe fallback\n  }\n  throw e;\n}","preventionTips":["Always pass lowercase slugs (usd, eur, cny, jpy), not display names.","Trim/lowercase/strip punctuation from user or env input before calling.","Map human-readable names ('US Dollar') to slugs ('usd') in a lookup table.","Remember: pattern-valid does not mean CoinGecko supports it — verify against its supported quote currencies."],"tags":["argument-validation","input-error","regex"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}