CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stricli--core

Build complex CLIs with type safety and no dependencies

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error types for argument parsing and validation with "did you mean?" suggestions.

Quick Reference

// Safe error handling in commands (return Error instead of throwing)
func: async function(flags) {
  try {
    await operation();
  } catch (err) {
    return new Error(`Operation failed: ${err.message}`);
  }
}

// Catch scanner errors
try {
  await run(app, inputs, { process });
} catch (err) {
  if (err instanceof ArgumentScannerError) {
    // Handle parsing errors
    console.error(err.message);
  }
}

ArgumentScannerError Hierarchy

All argument scanning errors extend ArgumentScannerError.

abstract class ArgumentScannerError extends Error

Error Types

ErrorWhenProperties
FlagNotFoundErrorUnknown flag nameinput, corrections, aliasName?
AliasNotFoundErrorUnknown alias letterinput
ArgumentParseErrorParser threw exceptionexternalFlagNameOrPlaceholder, input, exception
EnumValidationErrorInvalid enum valueexternalFlagName, input, values, corrections
UnsatisfiedFlagErrorMissing required flag valueexternalFlagName, nextFlagName?
UnsatisfiedPositionalErrorMissing required argumentplaceholder, limit?
UnexpectedPositionalErrorToo many argumentsexpectedCount, input
UnexpectedFlagErrorFlag specified multiple times (non-variadic)externalFlagName, previousInput, input
InvalidNegatedFlagSyntaxErrorValue provided for negated flagexternalFlagName, valueText

Example Scenarios

// FlagNotFoundError
// myapp --verbos
// Error: No flag registered for --verbos, did you mean --verbose?

// ArgumentParseError
// myapp --port abc
// Error: Failed to parse "abc" for port: Cannot convert abc to a number

// EnumValidationError
// myapp --env prodution
// Error: Expected "prodution" to be one of (dev|staging|prod), did you mean "prod"?

// UnsatisfiedFlagError
// myapp --output
// Error: Expected input for flag --output

// UnsatisfiedPositionalError
// myapp (expects 1 arg)
// Error: Expected argument for arg1

// UnexpectedPositionalError
// myapp file1 file2 file3 (expects max 2)
// Error: Too many arguments, expected 2 but encountered "file3"

Custom Error Formatting

import { formatMessageForArgumentScannerError } from "@stricli/core";

function handleError(error: ArgumentScannerError): string {
  return formatMessageForArgumentScannerError(error, {
    FlagNotFoundError: (err) => {
      if (err.corrections.length > 0) {
        return `Unknown flag: ${err.input}. Try: ${err.corrections.join(", ")}`;
      }
      return `Unknown flag: ${err.input}`;
    },
    ArgumentParseError: (err) => {
      return `Invalid value "${err.input}" for ${err.externalFlagNameOrPlaceholder}`;
    }
  });
}

try {
  await run(app, inputs, { process });
} catch (err) {
  if (err instanceof ArgumentScannerError) {
    const message = handleError(err);
    console.error(message);
  }
}

Safe Error Handling in Commands

Commands can return Error instead of throwing for graceful error handling.

const command = buildCommand({
  func: async function(flags, filePath) {
    try {
      const content = await readFile(filePath);
      this.process.stdout.write(content);
    } catch (err) {
      // Return Error - Stricli handles it gracefully
      return new Error(`Failed to read file: ${err.message}`);
    }
  },
  parameters: {
    positional: {
      kind: "tuple",
      parameters: [{ brief: "File path", parse: String }]
    }
  },
  docs: { brief: "Read file" }
});

Complete Example

import {
  buildApplication,
  buildCommand,
  run,
  ArgumentScannerError,
  FlagNotFoundError,
  ArgumentParseError,
  formatMessageForArgumentScannerError
} from "@stricli/core";

const app = buildApplication(
  buildCommand({
    func: async function(flags) {
      try {
        // Operation
        this.process.stdout.write("Success\n");
      } catch (err) {
        return new Error(`Operation failed: ${err.message}`);
      }
    },
    parameters: {
      flags: {
        port: {
          kind: "parsed",
          parse: (input) => {
            const port = parseInt(input, 10);
            if (isNaN(port) || port < 1 || port > 65535) {
              throw new Error("must be 1-65535");
            }
            return port;
          },
          brief: "Port"
        }
      }
    },
    docs: { brief: "Start server" }
  }),
  { name: "server" }
);

try {
  await run(app, process.argv.slice(2), { process });
} catch (err) {
  if (err instanceof ArgumentScannerError) {
    const message = formatMessageForArgumentScannerError(err, {
      FlagNotFoundError: (e) =>
        `ERROR: Flag "${e.input}" doesn't exist${
          e.corrections.length > 0 ? `. Did you mean: ${e.corrections.join(", ")}?` : ""
        }`,
      ArgumentParseError: (e) =>
        `ERROR: Invalid value "${e.input}" for --${e.externalFlagNameOrPlaceholder}. ${
          e.exception instanceof Error ? e.exception.message : ""
        }`
    });
    process.stderr.write(message + "\n");
    process.exitCode = 1;
  } else {
    throw err;
  }
}

Related

  • Parameter Parsers
  • Exit Codes

Install with Tessl CLI

npx tessl i tessl/npm-stricli--core

docs

application.md

commands-and-routing.md

configuration-and-context.md

documentation-and-help.md

error-handling.md

exit-codes.md

flag-parameters.md

index.md

parameter-parsers.md

positional-parameters.md

text-and-localization.md

tile.json