Documentation Index Fetch the complete documentation index at: https://mintlify.com/amanvarshney01/create-better-t-stack/llms.txt
Use this file to discover all available pages before exploring further.
The programmatic API uses typed errors with the Result type for type-safe error handling.
Error Types
All errors extend TaggedError from better-result and include a tag field for type discrimination.
CreateError
Union type for errors returned by create() and createVirtual():
type CreateError = UserCancelledError | CLIError | ProjectCreationError
Source : ~/workspace/source/apps/cli/src/index.ts:164
Error Classes
UserCancelledError
Thrown when the user cancels an operation.
class UserCancelledError extends TaggedError ( "UserCancelledError" ) {
tag : "UserCancelledError" ;
message : string ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:13
Example :
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr () && result . error . tag === "UserCancelledError" ) {
console . log ( "Operation cancelled by user" );
}
CLIError
General CLI error for validation failures, invalid flags, etc.
class CLIError extends TaggedError ( "CLIError" ) {
tag : "CLIError" ;
message : string ;
cause ?: unknown ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:24
Example :
const result = await create ( "my-app" , {
database: "postgres" ,
orm: "none" , // Invalid: postgres requires ORM
});
if ( result . isErr () && result . error . tag === "CLIError" ) {
console . error ( `Configuration error: ${ result . error . message } ` );
}
ProjectCreationError
Error during project scaffolding.
class ProjectCreationError extends TaggedError ( "ProjectCreationError" ) {
tag : "ProjectCreationError" ;
phase : string ; // Which phase failed (e.g., "templates", "dependencies")
message : string ;
cause ?: unknown ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:72
Example :
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr () && result . error . tag === "ProjectCreationError" ) {
console . error ( `Failed during ${ result . error . phase } : ${ result . error . message } ` );
if ( result . error . cause ) {
console . error ( "Caused by:" , result . error . cause );
}
}
ValidationError
Validation error for config/flag validation failures.
class ValidationError extends TaggedError ( "ValidationError" ) {
tag : "ValidationError" ;
field ?: string ;
value ?: unknown ;
message : string ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:31
Example :
import { ValidationError } from "create-better-t-stack" ;
if ( error instanceof ValidationError ) {
console . error ( `Invalid ${ error . field } : ${ error . message } ` );
console . error ( `Received value:` , error . value );
}
CompatibilityError
Error for incompatible option combinations.
class CompatibilityError extends TaggedError ( "CompatibilityError" ) {
tag : "CompatibilityError" ;
options : string []; // Conflicting options
message : string ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:43
Example :
import { CompatibilityError } from "create-better-t-stack" ;
if ( error instanceof CompatibilityError ) {
console . error ( `Incompatible options: ${ error . options . join ( ", " ) } ` );
console . error ( error . message );
}
DirectoryConflictError
Error when target directory exists and is not empty.
class DirectoryConflictError extends TaggedError ( "DirectoryConflictError" ) {
tag : "DirectoryConflictError" ;
directory : string ;
message : string ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:55
Example :
const result = await create ( "existing-dir" , {
directoryConflict: "error" , // Default
});
if ( result . isErr () && result . error . tag === "DirectoryConflictError" ) {
console . error ( `Directory exists: ${ result . error . directory } ` );
console . error ( "Use directoryConflict: 'overwrite', 'merge', or 'increment'" );
}
DatabaseSetupError
Error during database configuration.
class DatabaseSetupError extends TaggedError ( "DatabaseSetupError" ) {
tag : "DatabaseSetupError" ;
provider : string ; // Database provider (e.g., "postgres")
message : string ;
cause ?: unknown ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:83
Example :
import { DatabaseSetupError } from "create-better-t-stack" ;
if ( error instanceof DatabaseSetupError ) {
console . error ( `Failed to setup ${ error . provider } : ${ error . message } ` );
}
AddonSetupError
Error during addon configuration.
class AddonSetupError extends TaggedError ( "AddonSetupError" ) {
tag : "AddonSetupError" ;
addon : string ; // Addon name (e.g., "biome")
message : string ;
cause ?: unknown ;
}
Source : ~/workspace/source/apps/cli/src/utils/errors.ts:96
Example :
import { AddonSetupError } from "create-better-t-stack" ;
if ( error instanceof AddonSetupError ) {
console . error ( `Failed to setup ${ error . addon } : ${ error . message } ` );
}
GeneratorError
Error during template generation (used by createVirtual()).
class GeneratorError extends TaggedError ( "GeneratorError" ) {
tag : "GeneratorError" ;
message : string ;
phase ?: string ; // Generation phase that failed
cause ?: unknown ;
}
Source : ~/workspace/source/packages/template-generator/src/types.ts:41
Example :
const result = await createVirtual ({ /* ... */ });
if ( result . isErr () && result . error . tag === "GeneratorError" ) {
console . error ( `Generation failed: ${ result . error . message } ` );
if ( result . error . phase ) {
console . error ( `During phase: ${ result . error . phase } ` );
}
}
Error Handling Patterns
Using match()
The recommended pattern for handling errors:
import { create } from "create-better-t-stack" ;
const result = await create ( "my-app" , {
frontend: [ "tanstack-router" ],
backend: "hono" ,
});
result . match ({
ok : ( data ) => {
console . log ( `Success: ${ data . projectDirectory } ` );
},
err : ( error ) => {
console . error ( `Error: ${ error . message } ` );
process . exit ( 1 );
},
});
Using isErr() / isOk()
import { create } from "create-better-t-stack" ;
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr ()) {
console . error ( result . error . message );
process . exit ( 1 );
}
// TypeScript knows result.value is available here
console . log ( result . value . projectDirectory );
Type Discrimination with Tag
import { create } from "create-better-t-stack" ;
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr ()) {
const error = result . error ;
switch ( error . tag ) {
case "UserCancelledError" :
console . log ( "Operation cancelled" );
process . exit ( 0 );
case "DirectoryConflictError" :
console . error ( `Directory exists: ${ error . directory } ` );
console . error ( "Use --directory-conflict to handle this" );
process . exit ( 1 );
case "ValidationError" :
console . error ( `Invalid ${ error . field } : ${ error . message } ` );
process . exit ( 1 );
case "ProjectCreationError" :
console . error ( `Failed during ${ error . phase } : ${ error . message } ` );
process . exit ( 1 );
default :
console . error ( error . message );
process . exit ( 1 );
}
}
Using instanceof
import {
create ,
UserCancelledError ,
DirectoryConflictError ,
ValidationError ,
} from "create-better-t-stack" ;
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr ()) {
const error = result . error ;
if ( error instanceof UserCancelledError ) {
console . log ( "Cancelled" );
} else if ( error instanceof DirectoryConflictError ) {
console . error ( `Directory conflict: ${ error . directory } ` );
} else if ( error instanceof ValidationError ) {
console . error ( `Validation failed: ${ error . field } ` );
} else {
console . error ( error . message );
}
}
Using unwrapOr()
import { create } from "create-better-t-stack" ;
const result = await create ( "my-app" , { /* ... */ });
// Get value or null on error
const data = result . unwrapOr ( null );
if ( data ) {
console . log ( data . projectDirectory );
} else {
console . error ( "Failed to create project" );
}
Retrying on Error
import { create } from "create-better-t-stack" ;
async function createWithRetry ( name : string , options : any , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
const result = await create ( name , options );
if ( result . isOk ()) {
return result ;
}
if ( result . error . tag === "UserCancelledError" ) {
// Don't retry user cancellations
return result ;
}
console . error ( `Attempt ${ i + 1 } failed: ${ result . error . message } ` );
if ( i < maxRetries - 1 ) {
console . log ( `Retrying...` );
await new Promise ( resolve => setTimeout ( resolve , 1000 ));
}
}
}
Logging Errors
import { create } from "create-better-t-stack" ;
import fs from "node:fs" ;
const result = await create ( "my-app" , { /* ... */ });
if ( result . isErr ()) {
const error = result . error ;
// Log detailed error information
const errorLog = {
timestamp: new Date (). toISOString (),
type: error . tag ,
message: error . message ,
details: {
... error ,
},
};
fs . appendFileSync (
"error.log" ,
JSON . stringify ( errorLog , null , 2 ) + " \n "
);
console . error ( `Error logged. See error.log for details.` );
process . exit ( 1 );
}
Common Error Scenarios
Directory Already Exists
const result = await create ( "existing-dir" , {
directoryConflict: "error" , // Will throw DirectoryConflictError
});
// Solution: Use a different conflict strategy
const result2 = await create ( "existing-dir" , {
directoryConflict: "increment" , // Creates existing-dir-1, existing-dir-2, etc.
});
Invalid Configuration
const result = await create ( "my-app" , {
database: "postgres" ,
orm: "none" , // Invalid: postgres requires an ORM
});
// Will return CLIError or ValidationError
Missing Dependencies
const result = await create ( "my-app" , {
auth: "better-auth" ,
database: "none" , // Invalid: better-auth requires a database
});
// Will return CompatibilityError
Best Practices
Always Handle Errors Never ignore the Result type. Always check isOk() or use match() to handle both success and error cases.
Use Type Discrimination Use the tag field or instanceof to handle different error types appropriately.
Provide User Feedback For user-facing applications, provide clear error messages and suggested fixes.
Log Detailed Errors In production, log the full error object (including cause and phase) for debugging.