Download packages/node-sdk/src/errors.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 17.9 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/node-sdk/src/errors.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/node-sdk/src/errors.ts
-
curl -L -o errors.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/node-sdk/src/errors.ts
17.9 kB
| import { | |
| APIConnectionError, | |
| APIEmptyResponseError, | |
| APIProviderQuotaExhaustedError, | |
| APIStatusError, | |
| APITimeoutError, | |
| ChatProviderError, | |
| } from '@moonshot-ai/kosong'; | |
| export const ErrorCodes = { | |
| CONFIG_INVALID: 'config.invalid', | |
| SESSION_NOT_FOUND: 'session.not_found', | |
| SESSION_ALREADY_EXISTS: 'session.already_exists', | |
| SESSION_ID_INVALID: 'session.id_invalid', | |
| SESSION_ID_REQUIRED: 'session.id_required', | |
| SESSION_ID_EMPTY: 'session.id_empty', | |
| SESSION_TITLE_EMPTY: 'session.title_empty', | |
| SESSION_STATE_NOT_FOUND: 'session.state_not_found', | |
| SESSION_STATE_INVALID: 'session.state_invalid', | |
| SESSION_FORK_ACTIVE_TURN: 'session.fork_active_turn', | |
| SESSION_EXPORT_NOT_FOUND: 'session.export_not_found', | |
| SESSION_EXPORT_MISSING_VERSION: 'session.export_missing_version', | |
| SESSION_CLOSED: 'session.closed', | |
| SESSION_PERMISSION_MODE_INVALID: 'session.permission_mode_invalid', | |
| SESSION_THINKING_EMPTY: 'session.thinking_empty', | |
| SESSION_MODEL_EMPTY: 'session.model_empty', | |
| SESSION_PLAN_MODE_INVALID: 'session.plan_mode_invalid', | |
| SESSION_APPROVAL_HANDLER_ERROR: 'session.approval_handler_error', | |
| SESSION_QUESTION_HANDLER_ERROR: 'session.question_handler_error', | |
| SESSION_INIT_FAILED: 'session.init_failed', | |
| AGENT_NOT_FOUND: 'agent.not_found', | |
| TURN_AGENT_BUSY: 'turn.agent_busy', | |
| GOAL_ALREADY_EXISTS: 'goal.already_exists', | |
| GOAL_NOT_FOUND: 'goal.not_found', | |
| GOAL_OBJECTIVE_EMPTY: 'goal.objective_empty', | |
| GOAL_OBJECTIVE_TOO_LONG: 'goal.objective_too_long', | |
| GOAL_STATUS_INVALID: 'goal.status_invalid', | |
| GOAL_METADATA_RESERVED: 'goal.metadata_reserved', | |
| GOAL_NOT_RESUMABLE: 'goal.not_resumable', | |
| MODEL_NOT_CONFIGURED: 'model.not_configured', | |
| MODEL_CONFIG_INVALID: 'model.config_invalid', | |
| AUTH_LOGIN_REQUIRED: 'auth.login_required', | |
| CONTEXT_OVERFLOW: 'context.overflow', | |
| LOOP_MAX_STEPS_EXCEEDED: 'loop.max_steps_exceeded', | |
| PROVIDER_API_ERROR: 'provider.api_error', | |
| PROVIDER_FILTERED: 'provider.filtered', | |
| PROVIDER_RATE_LIMIT: 'provider.rate_limit', | |
| PROVIDER_AUTH_ERROR: 'provider.auth_error', | |
| PROVIDER_CONNECTION_ERROR: 'provider.connection_error', | |
| SKILL_NOT_FOUND: 'skill.not_found', | |
| SKILL_TYPE_UNSUPPORTED: 'skill.type_unsupported', | |
| SKILL_NAME_EMPTY: 'skill.name_empty', | |
| RECORDS_WRITE_FAILED: 'records.write_failed', | |
| COMPACTION_FAILED: 'compaction.failed', | |
| COMPACTION_UNABLE: 'compaction.unable', | |
| BACKGROUND_TASK_ID_EMPTY: 'task.task_id_empty', | |
| MCP_SERVER_NOT_FOUND: 'mcp.server_not_found', | |
| MCP_SERVER_DISABLED: 'mcp.server_disabled', | |
| MCP_STARTUP_FAILED: 'mcp.startup_failed', | |
| MCP_TOOL_NAME_COLLISION: 'mcp.tool_name_collision', | |
| MCP_OAUTH_FAILED: 'mcp.oauth_failed', | |
| PLUGIN_NOT_FOUND: 'plugin.not_found', | |
| PLUGIN_LOAD_FAILED: 'plugin.load_failed', | |
| REQUEST_INVALID: 'request.invalid', | |
| REQUEST_WORK_DIR_REQUIRED: 'request.work_dir_required', | |
| REQUEST_PROMPT_INPUT_EMPTY: 'request.prompt_input_empty', | |
| SHELL_GIT_BASH_NOT_FOUND: 'shell.git_bash_not_found', | |
| NOT_IMPLEMENTED: 'not_implemented', | |
| INTERNAL: 'internal', | |
| } as const; | |
| export type KimiErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]; | |
| export interface KimiErrorInfo { | |
| readonly title: string; | |
| readonly retryable: boolean; | |
| readonly public: boolean; | |
| readonly action?: string; | |
| } | |
| export const KIMI_ERROR_INFO = { | |
| 'config.invalid': { | |
| title: 'Invalid configuration', | |
| retryable: false, | |
| public: true, | |
| action: 'Check config.toml and provider/model settings.', | |
| }, | |
| 'session.not_found': { | |
| title: 'Session not found', | |
| retryable: false, | |
| public: true, | |
| action: 'Check the session id or list available sessions.', | |
| }, | |
| 'session.already_exists': { | |
| title: 'Session already exists', | |
| retryable: false, | |
| public: true, | |
| action: 'Use a different session id or remove the existing session first.', | |
| }, | |
| 'session.id_invalid': { | |
| title: 'Invalid session id', | |
| retryable: false, | |
| public: true, | |
| action: 'Use a session id without path-traversal characters.', | |
| }, | |
| 'session.id_required': { | |
| title: 'Session id required', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a session id when calling this method.', | |
| }, | |
| 'session.id_empty': { | |
| title: 'Session id is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty session id.', | |
| }, | |
| 'session.title_empty': { | |
| title: 'Session title is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty session title.', | |
| }, | |
| 'session.state_not_found': { | |
| title: 'Session state missing', | |
| retryable: false, | |
| public: true, | |
| action: 'The session directory is corrupted or missing state.json.', | |
| }, | |
| 'session.state_invalid': { | |
| title: 'Session state invalid', | |
| retryable: false, | |
| public: true, | |
| action: 'The session state.json is corrupted; remove the session or repair the file.', | |
| }, | |
| 'session.fork_active_turn': { | |
| title: 'Cannot fork session during active turn', | |
| retryable: true, | |
| public: true, | |
| action: 'Wait for the active turn to complete before forking.', | |
| }, | |
| 'session.export_not_found': { | |
| title: 'Session export directory missing', | |
| retryable: false, | |
| public: true, | |
| action: 'The session has not been persisted to disk yet.', | |
| }, | |
| 'session.export_missing_version': { | |
| title: 'Export version is missing', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a version when exporting the session.', | |
| }, | |
| 'session.closed': { | |
| title: 'Session is closed', | |
| retryable: false, | |
| public: true, | |
| action: 'Create a new session.', | |
| }, | |
| 'session.permission_mode_invalid': { | |
| title: 'Invalid permission mode', | |
| retryable: false, | |
| public: true, | |
| action: 'Use one of: yolo / manual / auto.', | |
| }, | |
| 'session.thinking_empty': { | |
| title: 'Thinking value is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty thinking option.', | |
| }, | |
| 'session.model_empty': { | |
| title: 'Model is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty model identifier.', | |
| }, | |
| 'session.plan_mode_invalid': { | |
| title: 'Invalid plan mode', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a boolean plan mode.', | |
| }, | |
| 'session.approval_handler_error': { | |
| title: 'Approval handler threw', | |
| retryable: false, | |
| public: true, | |
| action: 'Inspect the SDK approval handler for an unhandled exception.', | |
| }, | |
| 'session.question_handler_error': { | |
| title: 'Question handler threw', | |
| retryable: false, | |
| public: true, | |
| action: 'Inspect the SDK question handler for an unhandled exception.', | |
| }, | |
| 'session.init_failed': { | |
| title: 'Session init failed', | |
| retryable: false, | |
| public: false, | |
| action: 'Review the init failure details and try again.', | |
| }, | |
| 'agent.not_found': { | |
| title: 'Agent not found', | |
| retryable: false, | |
| public: true, | |
| action: 'Check the agent id or list available agents.', | |
| }, | |
| 'turn.agent_busy': { | |
| title: 'Agent is busy', | |
| retryable: true, | |
| public: true, | |
| action: 'Wait for the current turn to finish or steer it.', | |
| }, | |
| 'goal.already_exists': { | |
| title: 'A goal is already active', | |
| retryable: false, | |
| public: true, | |
| action: 'Use `/goal replace <objective>` to replace the current goal.', | |
| }, | |
| 'goal.not_found': { | |
| title: 'No goal found', | |
| retryable: false, | |
| public: true, | |
| action: 'Start a goal with `/goal <objective>` first.', | |
| }, | |
| 'goal.objective_empty': { | |
| title: 'Goal objective is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty objective.', | |
| }, | |
| 'goal.objective_too_long': { | |
| title: 'Goal objective is too long', | |
| retryable: false, | |
| public: true, | |
| action: 'Keep the objective under 4000 characters; reference long details by file path.', | |
| }, | |
| 'goal.status_invalid': { | |
| title: 'Invalid goal status transition', | |
| retryable: false, | |
| public: true, | |
| action: 'Use a status allowed for this actor (complete, blocked, or impossible).', | |
| }, | |
| 'goal.metadata_reserved': { | |
| title: 'Goal metadata is reserved', | |
| retryable: false, | |
| public: true, | |
| action: 'Do not write metadata.custom.goal directly; use the goal lifecycle methods.', | |
| }, | |
| 'goal.not_resumable': { | |
| title: 'Goal is not resumable', | |
| retryable: false, | |
| public: true, | |
| action: 'Only paused goals can be resumed.', | |
| }, | |
| 'model.not_configured': { | |
| title: 'No model configured', | |
| retryable: false, | |
| public: true, | |
| action: 'Set a default model in config.toml or via setModel.', | |
| }, | |
| 'model.config_invalid': { | |
| title: 'Invalid model configuration', | |
| retryable: false, | |
| public: true, | |
| action: 'Check the model and provider entries in config.toml.', | |
| }, | |
| 'auth.login_required': { | |
| title: 'Login required', | |
| retryable: false, | |
| public: true, | |
| action: 'Run the login flow for the provider before retrying.', | |
| }, | |
| 'context.overflow': { | |
| title: 'Context window overflow', | |
| retryable: true, | |
| public: true, | |
| action: 'Compact the conversation or start a new session.', | |
| }, | |
| 'loop.max_steps_exceeded': { | |
| title: 'Turn exceeded max steps', | |
| retryable: false, | |
| public: true, | |
| action: 'Increase loop_control.max_steps_per_turn in config.toml or split the task.', | |
| }, | |
| 'provider.api_error': { | |
| title: 'Provider API error', | |
| retryable: false, | |
| public: true, | |
| action: 'Inspect details.statusCode / details.requestId; check provider status.', | |
| }, | |
| 'provider.filtered': { | |
| title: 'Provider filtered response', | |
| retryable: false, | |
| public: true, | |
| action: 'Revise the prompt or model configuration to avoid provider safety filtering.', | |
| }, | |
| 'provider.rate_limit': { | |
| title: 'Provider rate limit', | |
| retryable: true, | |
| public: true, | |
| action: 'Retry after a delay or reduce request frequency.', | |
| }, | |
| 'provider.auth_error': { | |
| title: 'Provider authentication error', | |
| retryable: false, | |
| public: true, | |
| action: 'Re-authenticate with the provider.', | |
| }, | |
| 'provider.connection_error': { | |
| title: 'Provider connection error', | |
| retryable: true, | |
| public: true, | |
| action: 'Check network connectivity and retry.', | |
| }, | |
| 'skill.not_found': { | |
| title: 'Skill not found', | |
| retryable: false, | |
| public: true, | |
| action: 'List available skills via the skill registry.', | |
| }, | |
| 'skill.type_unsupported': { | |
| title: 'Skill type not supported', | |
| retryable: false, | |
| public: true, | |
| action: 'Only inline skills can be activated by the user.', | |
| }, | |
| 'skill.name_empty': { | |
| title: 'Skill name is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty skill name.', | |
| }, | |
| 'records.write_failed': { | |
| title: 'Failed to write records', | |
| retryable: true, | |
| public: true, | |
| action: 'Check disk space and permissions on the session directory.', | |
| }, | |
| 'compaction.failed': { | |
| title: 'Compaction failed', | |
| retryable: false, | |
| public: true, | |
| action: 'Inspect logs and consider increasing compaction limits.', | |
| }, | |
| 'compaction.unable': { | |
| title: 'Unable to compact', | |
| retryable: false, | |
| public: true, | |
| action: 'The current history has no prefix that can be compacted (e.g. only a pending user message). Start a new turn or session instead.', | |
| }, | |
| 'task.task_id_empty': { | |
| title: 'Background task id is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide a non-empty task id.', | |
| }, | |
| 'mcp.server_not_found': { | |
| title: 'MCP server not found', | |
| retryable: false, | |
| public: true, | |
| action: 'List configured MCP servers and check the requested name.', | |
| }, | |
| 'mcp.server_disabled': { | |
| title: 'MCP server is disabled', | |
| retryable: false, | |
| public: true, | |
| action: 'Enable the MCP server entry in config before reconnecting.', | |
| }, | |
| 'mcp.startup_failed': { | |
| title: 'MCP server startup failed', | |
| retryable: true, | |
| public: true, | |
| action: 'Inspect the MCP server log or call reconnect once the server is healthy.', | |
| }, | |
| 'mcp.tool_name_collision': { | |
| title: 'MCP tool name collision', | |
| retryable: false, | |
| public: true, | |
| action: 'Rename one of the colliding MCP tools or servers so their qualified names are unique.', | |
| }, | |
| 'mcp.oauth_failed': { | |
| title: 'MCP OAuth authorization failed', | |
| retryable: true, | |
| public: true, | |
| action: 'Begin the authorization flow again; inspect the error details if it keeps failing.', | |
| }, | |
| 'plugin.not_found': { | |
| title: 'Plugin not found', | |
| retryable: false, | |
| public: true, | |
| action: 'List installed plugins via /plugins and check the requested id.', | |
| }, | |
| 'plugin.load_failed': { | |
| title: 'Plugin state failed to load', | |
| retryable: true, | |
| public: true, | |
| action: 'Fix the installed.json file under $KIMI_CODE_HOME/plugins/ and run /plugins reload.', | |
| }, | |
| 'request.invalid': { | |
| title: 'Invalid request', | |
| retryable: false, | |
| public: true, | |
| action: 'Check the input shape matches the API contract.', | |
| }, | |
| 'request.work_dir_required': { | |
| title: 'workDir is required', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide workDir in the request payload.', | |
| }, | |
| 'request.prompt_input_empty': { | |
| title: 'Prompt input is empty', | |
| retryable: false, | |
| public: true, | |
| action: 'Provide non-empty prompt input.', | |
| }, | |
| 'shell.git_bash_not_found': { | |
| title: 'Git Bash not found', | |
| retryable: false, | |
| public: true, | |
| action: 'Install Git for Windows from https://gitforwindows.org/ or set KIMI_SHELL_PATH to a bash.exe.', | |
| }, | |
| not_implemented: { | |
| title: 'Not implemented', | |
| retryable: false, | |
| public: true, | |
| action: 'This feature is not implemented yet.', | |
| }, | |
| internal: { | |
| title: 'Internal error', | |
| retryable: false, | |
| public: true, | |
| action: 'Inspect logs or report the issue with diagnostics.', | |
| }, | |
| } as const satisfies Record<KimiErrorCode, KimiErrorInfo>; | |
| export function isKimiErrorCode(code: unknown): code is KimiErrorCode { | |
| return typeof code === 'string' && Object.hasOwn(KIMI_ERROR_INFO, code); | |
| } | |
| export interface KimiErrorOptions { | |
| readonly details?: Record<string, unknown>; | |
| readonly cause?: unknown; | |
| } | |
| export class KimiError extends Error { | |
| readonly code: KimiErrorCode; | |
| readonly details?: Record<string, unknown>; | |
| override readonly cause?: unknown; | |
| constructor(code: KimiErrorCode, message: string, options: KimiErrorOptions = {}) { | |
| super(message); | |
| this.name = 'KimiError'; | |
| this.code = code; | |
| this.details = options.details; | |
| this.cause = options.cause; | |
| } | |
| } | |
| export interface KimiErrorPayload { | |
| readonly code: KimiErrorCode; | |
| readonly message: string; | |
| readonly name?: string; | |
| readonly details?: Record<string, unknown>; | |
| readonly retryable: boolean; | |
| } | |
| export function isKimiError(error: unknown): error is KimiError { | |
| return error instanceof KimiError; | |
| } | |
| export function makeErrorPayload( | |
| code: KimiErrorCode, | |
| message: string, | |
| options?: { readonly details?: Record<string, unknown>; readonly name?: string }, | |
| ): KimiErrorPayload { | |
| return { | |
| code, | |
| message, | |
| name: options?.name, | |
| details: options?.details, | |
| retryable: KIMI_ERROR_INFO[code].retryable, | |
| }; | |
| } | |
| export function toKimiErrorPayload(error: unknown): KimiErrorPayload { | |
| if (isKimiError(error)) { | |
| return { | |
| code: error.code, | |
| message: error.message, | |
| name: error.name, | |
| details: error.details, | |
| retryable: KIMI_ERROR_INFO[error.code].retryable, | |
| }; | |
| } | |
| if (error instanceof APIStatusError) { | |
| const code: KimiErrorCode = | |
| error instanceof APIProviderQuotaExhaustedError | |
| ? ErrorCodes.PROVIDER_API_ERROR | |
| : error.statusCode === 429 | |
| ? ErrorCodes.PROVIDER_RATE_LIMIT | |
| : error.statusCode === 401 | |
| ? ErrorCodes.PROVIDER_AUTH_ERROR | |
| : ErrorCodes.PROVIDER_API_ERROR; | |
| return { | |
| code, | |
| message: sanitizeStatusErrorMessage(error.message), | |
| name: error.name, | |
| details: { | |
| statusCode: error.statusCode, | |
| requestId: error.requestId, | |
| }, | |
| retryable: KIMI_ERROR_INFO[code].retryable, | |
| }; | |
| } | |
| if (error instanceof APIConnectionError || error instanceof APITimeoutError) { | |
| return { | |
| code: ErrorCodes.PROVIDER_CONNECTION_ERROR, | |
| message: error.message, | |
| name: error.name, | |
| retryable: KIMI_ERROR_INFO[ErrorCodes.PROVIDER_CONNECTION_ERROR].retryable, | |
| }; | |
| } | |
| if (error instanceof APIEmptyResponseError) { | |
| const code = | |
| error.finishReason === 'filtered' | |
| ? ErrorCodes.PROVIDER_FILTERED | |
| : ErrorCodes.PROVIDER_API_ERROR; | |
| return { | |
| code, | |
| message: error.message, | |
| name: error.name, | |
| details: { | |
| finishReason: error.finishReason, | |
| rawFinishReason: error.rawFinishReason, | |
| }, | |
| retryable: KIMI_ERROR_INFO[code].retryable, | |
| }; | |
| } | |
| if (error instanceof ChatProviderError) { | |
| return { | |
| code: ErrorCodes.PROVIDER_API_ERROR, | |
| message: error.message, | |
| name: error.name, | |
| retryable: KIMI_ERROR_INFO[ErrorCodes.PROVIDER_API_ERROR].retryable, | |
| }; | |
| } | |
| if (error instanceof Error) { | |
| return { | |
| code: ErrorCodes.INTERNAL, | |
| message: error.message, | |
| name: error.name, | |
| retryable: KIMI_ERROR_INFO[ErrorCodes.INTERNAL].retryable, | |
| }; | |
| } | |
| return { | |
| code: ErrorCodes.INTERNAL, | |
| message: String(error), | |
| retryable: KIMI_ERROR_INFO[ErrorCodes.INTERNAL].retryable, | |
| }; | |
| } | |
| function sanitizeStatusErrorMessage(message: string): string { | |
| const titleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(message); | |
| const extracted = titleMatch?.[1]?.trim(); | |
| const normalized = extracted !== undefined && extracted.length > 0 ? extracted : message; | |
| return normalized.replaceAll('\r', ''); | |
| } | |
| export function fromKimiErrorPayload(payload: KimiErrorPayload): KimiError { | |
| return new KimiError(payload.code, payload.message, { | |
| details: payload.details, | |
| }); | |
| } | |