How to Troubleshoot Common Issues in TaxHacker: Complete Diagnostics Guide

Most TaxHacker runtime errors originate in five subsystems: file uploads and storage, AI extraction services, Better-Auth configuration, Stripe payment processing, or export and backup operations.

TaxHacker is a self-hosted AI-powered accounting application built with Next.js 15+, Prisma, and dedicated microservices for document processing and payments. When you need to troubleshoot common issues in TaxHacker, understanding the specific file paths and validation logic in its codebase allows you to pinpoint failures quickly without extensive debugging.

File Uploads and Storage Issues

File handling represents the most frequent source of runtime errors in TaxHacker. The upload flow begins at app/(app)/unsorted/actions.ts, which delegates path computations and storage validation to lib/files.ts.

Path Traversal Protection

The safePathJoin function in lib/files.ts (lines 56-57) aggressively validates paths to prevent directory traversal attacks. If you encounter a "Path traversal detected" error, the filename contains ../ sequences or other dangerous characters attempting to escape the user upload directory.

// Verify safePathJoin behavior
import { safePathJoin } from '@/lib/files'

const userPath = '/uploads/user@example.com'
console.log(safePathJoin(userPath, '../evil.txt')) // throws "Path traversal detected"

Sanitize client-side filenames before transmission by stripping path components:

function cleanFilename(name: string): string {
  return name.replace(/\.\.+/g, '').replace(/[<>:"/\\|?*]/g, '_')
}

Storage Quota Violations

The "Insufficient storage" error triggers when isEnoughStorageToUploadFile (lines 88-93 in lib/files.ts) detects the user has exceeded their limit. Verify current usage against the database quota:

import { getDirectorySize, getUserUploadsDirectory } from '@/lib/files'
import { prisma } from '@/prisma/client'

async function checkUserQuota(userId: number) {
  const user = await prisma.users.findUnique({ where: { id: userId } })
  const dir = getUserUploadsDirectory(user)
  const used = await getDirectorySize(dir)
  console.log('Used:', used, 'Limit:', user.storageLimit)
}

To increase a user's storage limit via Prisma:

await prisma.users.update({
  where: { id: userId },
  data: { storageLimit: 10 * 1024 * 1024 * 1024 } // 10 GB
})

Missing File on Disk Errors

If uploads fail with "File not found on disk" errors, check lines 97-98 in app/(app)/unsorted/actions.ts. This usually indicates an interrupted write or desynchronization between the database record and the physical file in the UPLOAD_PATH directory. The error can also originate from ai/attachments.ts (line 18) when the preview generator expects files that were never saved to the unsorted/ or previews/ subdirectories.

AI Extraction and Analysis Failures

TaxHacker's document analysis relies on providers configured in lib/llm-providers.ts and orchestrated through ai/analyze.ts.

Provider Authentication Errors

The "Failed to analyze invoice" error typically originates from lines 32-33 in ai/analyze.ts, where the code throws when a provider returns an error payload. Verify your API keys are loaded correctly in lib/config.ts:

import config from '@/lib/config'

console.log('OpenAI key:', config.ai.openaiApiKey?.slice(0, 4))
if (!config.ai.openaiApiKey) {
  throw new Error('OPENAI_API_KEY is missing – set it in .env')
}

Required environment variables include OPENAI_API_KEY, GOOGLE_API_KEY, or MISTRAL_API_KEY as defined in the configuration schema.

Currency Formatting Edge Cases

The formatCurrency utility in lib/utils.ts (lines 21-24) falls back to plain strings when Intl.NumberFormat throws for unsupported currency codes. If you see "Unsupported currency" warnings in logs, the invoice likely contains an obscure cryptocurrency or invalid ISO code that cannot be processed by the standard formatting library.

Authentication and Self-Hosted Mode Errors

TaxHacker uses Better-Auth for authentication, configured in lib/auth.ts with environment variables validated in lib/config.ts.

Secret Validation Failures

The BETTER_AUTH_SECRET must be at least 16 characters long. Lines 13-16 in lib/config.ts enforce this minimum length during application startup. If authentication fails with cryptic errors, verify your secret meets the requirement:

import config from '@/lib/config'

if (!config.auth.secret || config.auth.secret.length < 16) {
  console.error('Auth secret must be ≥16 characters – check BETTER_AUTH_SECRET')
}

User Not Found Errors

The error "User with this email does not exist" maps directly to lines 58-59 in lib/auth.ts, where the system raises APIError("NOT_FOUND") for unknown login attempts.

Unexpected Signup Disabling

In self-hosted mode, signup disables automatically when SELF_HOSTED_MODE=true or DISABLE_SIGNUP=true. Check lines 64-66 in lib/config.ts to understand the boolean logic affecting auth.disableSignup. If you see "Signup disabled unexpectedly", verify these environment variables are set according to your deployment intentions.

Stripe Integration Failures

Payment processing errors typically surface in app/api/stripe/checkout/route.ts or the webhook handler at app/api/stripe/webhook/route.ts.

Checkout Session Failures

Lines 42-48 in the checkout route catch Stripe SDK exceptions and return JSON error responses. The "Failed to create Stripe checkout session" message indicates missing or invalid STRIPE_SECRET_KEY configuration.

Verify your keys are loaded:

import config from '@/lib/config'
console.log('Stripe secret:', config.stripe.secretKey?.slice(0, 4))

Webhook Plan Mapping Errors

The webhook handler (lines 82-83) throws "Plan not found for price ID" when Stripe sends a price ID not present in the local plan configuration. Ensure your production price IDs match the environment variables in lib/config.ts. For "Invalid webhook signature" errors, verify the STRIPE_WEBHOOK_SECRET environment variable matches your Stripe dashboard configuration.

Export and Backup Malfunctions

Data export and backup functionality relies on file system writes to temporary directories.

ZIP Creation Failures

The "Failed to create zip folder" error at line 96 in app/(app)/export/transactions/route.ts and line 54 in app/(app)/settings/backups/data/route.ts indicates permission problems with os.tmpdir() or the UPLOAD_PATH directory.

Check your temporary directory permissions:

import os from 'os'
console.log('Tmp dir:', os.tmpdir())

If running in Docker, ensure the container user has write access or override the path:


# In docker-compose.yml

environment:
  - TMPDIR=/app/tmp
volumes:
  - ./tmp:/app/tmp

General Error Debugging Techniques

TaxHacker centralizes error handling in lib/utils.ts. The fetchAsBase64 function (lines 84-85 and 95-98) demonstrates the standard pattern: network errors throw with "Error fetching image as data URL", which appears in server logs when preview generation fails for remote images or when network connectivity is interrupted.

Enable verbose logging by inspecting caught errors in API routes:

try {
  await someAsyncOperation()
} catch (e) {
  console.error('Unexpected error:', e)
  return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}

Always ensure your deployment captures stdout and stderr via Docker logs or systemd journal to view these diagnostic messages.

Summary

  • File upload errors usually indicate path traversal attempts blocked by safePathJoin (lines 56-57) in lib/files.ts or quota violations detected by isEnoughStorageToUploadFile (lines 88-93).
  • AI extraction failures require verifying OPENAI_API_KEY, GOOGLE_API_KEY, or MISTRAL_API_KEY in lib/config.ts and checking provider response handling in ai/analyze.ts (lines 32-33).
  • Authentication issues stem from short BETTER_AUTH_SECRET values (minimum 16 characters, lines 13-16 in lib/config.ts) or self-hosted mode configuration affecting signup in lib/auth.ts (lines 58-59).
  • Stripe errors map to missing price IDs in app/api/stripe/webhook/route.ts (lines 82-83) or invalid checkout session configurations in app/api/stripe/checkout/route.ts (lines 42-48).
  • Export and backup failures indicate os.tmpdir() permission problems or missing write access to the UPLOAD_PATH directory at app/(app)/export/transactions/route.ts (line 96).

Frequently Asked Questions

Why does TaxHacker show "Path traversal detected" when uploading documents?

This security error originates in lib/files.ts lines 56-57 where safePathJoin detects ../ patterns attempting to escape the user upload directory. Clean filenames on the client by removing path separators and special characters before transmission, or check that the UPLOAD_PATH environment variable points to a valid, writable directory.

How do I fix "Failed to analyze invoice" errors during AI processing?

First verify your provider API keys are loaded in lib/config.ts and meet the requirements for OPENAI_API_KEY, GOOGLE_API_KEY, or MISTRAL_API_KEY. Then check server logs for the specific error message thrown at lines 32-33 in ai/analyze.ts, which contains the provider's detailed rejection reason such as quota limits or invalid authentication.

What causes "Insufficient storage" errors in self-hosted deployments?

The isEnoughStorageToUploadFile function in lib/files.ts (lines 88-93) compares user.storageUsed plus the incoming file size against user.storageLimit. Run getDirectorySize on the user's upload folder to verify actual disk usage, then update the storageLimit column in the Prisma users table if the user has legitimately exceeded their quota.

Why are Stripe webhooks failing with "Plan not found for price ID"?

The webhook handler in app/api/stripe/webhook/route.ts (lines 82-83) cannot map the Stripe price.id to a local plan configuration. Ensure your environment variables contain the correct price IDs for your production Stripe products, and verify the STRIPE_WEBHOOK_SECRET matches your Stripe dashboard configuration to prevent signature validation failures.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →