Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/cli/src/flags.mts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ function getRawSpaceSizeFlags(): RawSpaceSizeFlags {
// Validate numeric flags (should be guaranteed by meow type='number', but defensive).
if (Number.isNaN(maxOldSpaceSize) || maxOldSpaceSize < 0) {
throw new Error(
`Invalid value for --max-old-space-size: ${cli.flags['maxOldSpaceSize']}`,
`--max-old-space-size must be a non-negative integer in megabytes (saw: "${cli.flags['maxOldSpaceSize']}"); pass a whole number like --max-old-space-size=4096 for 4GB`,
)
}
if (Number.isNaN(maxSemiSpaceSize) || maxSemiSpaceSize < 0) {
throw new Error(
`Invalid value for --max-semi-space-size: ${cli.flags['maxSemiSpaceSize']}`,
`--max-semi-space-size must be a non-negative integer in megabytes (saw: "${cli.flags['maxSemiSpaceSize']}"); pass a whole number like --max-semi-space-size=128`,
)
}

Expand Down
11 changes: 7 additions & 4 deletions packages/cli/src/utils/basics/vfs-extract.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createHash } from 'node:crypto'
import { homedir } from 'node:os'
import path from 'node:path'

import { joinAnd } from '@socketsecurity/lib/arrays'
import { getDefaultLogger } from '@socketsecurity/lib/logger'
import { normalizePath } from '@socketsecurity/lib/paths/normalize'
import { spawn } from '@socketsecurity/lib/spawn'
Expand Down Expand Up @@ -176,7 +177,7 @@ export async function extractBasicsTools(
const missingTools = tools.filter(t => !extractedPaths[t])
if (missingTools.length) {
throw new Error(
`Failed to extract all basics tools. Missing: ${missingTools.join(', ')}`,
`socket-basics VFS extraction returned ${Object.keys(extractedPaths).length}/${tools.length} tools (missing: ${joinAnd(missingTools)}); the SEA bundle is incomplete — rebuild with all basics tools included`,
)
}

Expand All @@ -186,7 +187,9 @@ export async function extractBasicsTools(
const pythonExe = isPlatWin ? 'python3.exe' : 'python3'
const pythonDir = extractedPaths['python']
if (!pythonDir) {
throw new Error('Python extraction path not found')
throw new Error(
`extractedPaths.python is undefined after VFS extraction (expected a directory path); the basics SEA bundle is missing Python — rebuild the SEA binary`,
)
}
const pythonPath = normalizePath(path.join(pythonDir, 'bin', pythonExe))

Expand All @@ -197,7 +200,7 @@ export async function extractBasicsTools(

if (!validateResult || validateResult.code !== 0) {
throw new Error(
`Python validation failed: ${validateResult?.stderr || 'Unable to execute Python'}`,
`extracted Python at ${pythonPath} failed to run with exit code ${validateResult?.code ?? 'null'} (stderr: ${validateResult?.stderr || '<none>'}); the extracted binary may be corrupt or missing a shared lib — rebuild the SEA binary`,
)
}

Expand All @@ -218,7 +221,7 @@ export async function extractBasicsTools(

if (!toolValidateResult || toolValidateResult.code !== 0) {
throw new Error(
`${tool} validation failed: ${toolValidateResult?.stderr || `Unable to execute ${tool}`}`,
`extracted ${tool} at ${toolPath} failed to run with exit code ${toolValidateResult?.code ?? 'null'} (stderr: ${toolValidateResult?.stderr || '<none>'}); the extracted binary may be corrupt or missing a shared lib — rebuild the SEA binary`,
)
}

Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/utils/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ function getConfigValues(retryCount = 0): LocalConfig {
const decoded = Buffer.from(rawString, 'base64').toString('utf8')
// Check for invalid UTF-8 sequences (replacement character).
if (decoded.includes('\ufffd')) {
throw new Error('Invalid UTF-8 in base64-encoded config')
throw new Error(
`SOCKET_CLI_CONFIG contains invalid UTF-8 after base64-decode (replacement-character in output); the env var may have been truncated or double-encoded — re-export it with \`echo '{...}' | base64\``,
)
}
const parsed = JSON.parse(decoded)
// Only copy supported config keys to prevent prototype pollution.
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/utils/fs/path-resolve.mts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function findNpmDirPathSync(npmBinPath: string): string | undefined {
while (true) {
if (iterations >= MAX_ITERATIONS) {
throw new Error(
`path traversal exceeded maximum iterations of ${MAX_ITERATIONS}`,
`npm path resolution walked ${MAX_ITERATIONS} directories without finding lib/node_modules/npm starting from "${npmBinPath}" (current: "${thePath}"); check for a circular symlink or corrupt node install`,
)
}
iterations += 1
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/utils/git/gitlab-provider.mts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class GitLabProvider implements PrProvider {
}

throw new Error(
`Failed to create merge request after ${retries} attempts: ${owner}/${repo}#${head}`,
`GitLab API rejected createMergeRequest for ${owner}/${repo} (head="${head}") after ${retries} attempts with exponential backoff; check GITLAB_TOKEN permissions (needs api scope), that the target branch exists, and that GitLab is reachable`,
)
}

Expand Down Expand Up @@ -326,6 +326,6 @@ function getGitLabToken(): string {
}

throw new Error(
'GitLab token not found. Set GITLAB_TOKEN environment variable.',
`GitLab access requires a token but process.env.GITLAB_TOKEN is not set; create a personal access token with the \`api\` scope at https://gitlab.com/-/user_settings/personal_access_tokens and export GITLAB_TOKEN=<token>`,
)
}
4 changes: 3 additions & 1 deletion packages/cli/src/utils/git/operations.mts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ async function getGitPath(): Promise<string> {
if (!_gitPath) {
const result = await whichReal('git', { nothrow: true })
if (!result || Array.isArray(result)) {
throw new Error('git not found in PATH')
throw new Error(
`git executable not found on PATH (whichReal returned ${Array.isArray(result) ? 'multiple matches' : 'null'}); install git (e.g. \`brew install git\`, \`apt install git\`) and make sure it is reachable on PATH`,
)
}
_gitPath = result
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/utils/npm/spec.mts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ export function safeNpmSpecToPurl(pkgSpec: string): string | undefined {
export function npmSpecToPurl(pkgSpec: string): string {
const purl = safeNpmSpecToPurl(pkgSpec)
if (!purl) {
throw new Error(`Failed to convert ${NPM} spec to PURL: ${pkgSpec}`)
throw new Error(
`cannot convert npm spec "${pkgSpec}" to PURL (safeNpmSpecToPurl returned null); valid npm specs look like "lodash@4.17.21" or "@scope/pkg@^1.0.0" — check the spec for typos or unsupported forms`,
)
}
return purl
}
4 changes: 3 additions & 1 deletion packages/cli/src/utils/promise/queue.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export class PromiseQueue {
this.maxQueueLength = maxQueueLength
}
if (maxConcurrency < 1) {
throw new Error('maxConcurrency must be at least 1')
throw new Error(
`PromiseQueue maxConcurrency must be >= 1 (saw: ${maxConcurrency}); pass a positive integer like 4 or 8`,
)
}
}

Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/utils/terminal/iocraft.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import { createRequire } from 'node:module'

import { getErrorCause } from '../error/errors.mts'

import type iocraft from '@socketaddon/iocraft'

// Re-export iocraft types for direct access when needed.
Expand All @@ -27,8 +29,7 @@ function getIocraft(): typeof iocraft {
iocraftInstance = loaded.default || loaded
} catch (e) {
throw new Error(
`Failed to load iocraft native module: ${e}\n` +
`Make sure @socketaddon/iocraft is installed and your platform is supported.`,
`could not load @socketaddon/iocraft native module (${getErrorCause(e)}); reinstall socket-cli to pull the prebuilt for your platform, or check that your platform (${process.platform}-${process.arch}) has a published prebuilt`,
)
}
}
Expand Down
10 changes: 7 additions & 3 deletions packages/cli/test/unit/utils/git/gitlab-provider.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('git/gitlab-provider', () => {
it('throws error when no token available', () => {
delete process.env['GITLAB_TOKEN']
expect(() => new GitLabProvider()).toThrow(
'GitLab token not found. Set GITLAB_TOKEN environment variable.',
/GitLab access requires a token but process\.env\.GITLAB_TOKEN is not set/,
)
})
})
Expand Down Expand Up @@ -193,7 +193,9 @@ describe('git/gitlab-provider', () => {
retries: 2,
title: 'Test',
}),
).rejects.toThrow('Failed to create merge request after 2 attempts')
).rejects.toThrow(
/GitLab API rejected createMergeRequest for owner\/repo .*after 2 attempts/,
)
})

it('does not retry on 400 errors', async () => {
Expand All @@ -212,7 +214,9 @@ describe('git/gitlab-provider', () => {
retries: 3,
title: 'Test',
}),
).rejects.toThrow('Failed to create merge request after 3 attempts')
).rejects.toThrow(
/GitLab API rejected createMergeRequest for owner\/repo .*after 3 attempts/,
)

expect(mockCreate).toHaveBeenCalledTimes(1)
})
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/unit/utils/npm/spec.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ describe('npm-spec utilities', () => {

// Make the fallback parsing fail by providing an empty string that would result in empty name.
expect(() => npmSpecToPurl('')).toThrow(
'Failed to convert npm spec to PURL:',
/cannot convert npm spec/,
)
})

Expand All @@ -467,7 +467,7 @@ describe('npm-spec utilities', () => {

// Make fallback parsing fail by providing empty string.
expect(() => npmSpecToPurl('')).toThrow(
'Failed to convert npm spec to PURL: ',
/cannot convert npm spec ""/,
)
})

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/unit/utils/promise/queue.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ describe('PromiseQueue', () => {

it('should throw error for invalid concurrency', () => {
expect(() => new PromiseQueue(0)).toThrow(
'maxConcurrency must be at least 1',
/PromiseQueue maxConcurrency must be >= 1 \(saw: 0\)/,
)
expect(() => new PromiseQueue(-1)).toThrow(
'maxConcurrency must be at least 1',
/PromiseQueue maxConcurrency must be >= 1 \(saw: -1\)/,
)
})
})