Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Why Your AI Side-Hustle Script Won't Run: 5 JavaScript Config Failures That Break Claude/OpenAI Tools (and How to Isolate Node v

If you've ever copied a working AI automation script, run node tool.js, and watched it die with Cannot use import statement outside a module or fetch is not defined — this article gets you unstuck. By the end you'll have a 30-second d…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

If you've ever copied a working AI automation script, run node tool.js, and watched it die with Cannot use import statement outside a module or fetch is not defined — this article gets you unstuck. By the end you'll have a 30-second decision tree to tell whether the bug lives in your package.json, your tsconfig.json, or your runtime, plus two copy-paste diagnostic scripts that print exactly which environment you're in and why your imports resolved the way they did.



These five failures account for the majority of "the code is identical but it won't run on my machine" reports I see in AI-tooling repos. None of them are logic bugs. They're all configuration mismatches between how your code is written and how the JavaScript engine is told to read it.






Failure 1: Cannot use import statement — the package.json type field vs Claude SDK examples



The single most common breakage. You grab a snippet from the Anthropic SDK README:




import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

const msg = await client.messages.create({
model: 'claude-opus-4-8',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Say hi' }],
});
console.log(msg.content[0].text);






You save it as tool.js, run node tool.js, and get SyntaxError: Cannot use import statement outside a module. The code is correct. The problem is that Node defaults every .js file to CommonJS unless told otherwise. The SDK docs assume ESM.



The fix is one line in package.json:




{
"name": "ai-side-tool",
"type": "module",
"dependencies": {
"@anthropic-ai/sdk": "^0.30.0"
}
}






The gotcha nobody mentions: once you set "type": "module", every require() in your other helper files breaks with require is not defined in ES module scope. So if your repo mixes a copied ESM SDK example with your own require('./config') helpers, fixing failure 1 immediately triggers a second wave of errors. The clean rule: pick one module system per package. If you must keep a CommonJS file inside a "type": "module" package, rename it to .cjs. If you need ESM in a CommonJS package, rename it to .mjs. The file extension overrides the package.json default — that's the escape hatch.






Failure 2: fetch is not defined — assuming the browser's global in Node



A huge amount of AI automation code is written assuming fetch exists globally, because every example was tested in a browser console or a recent runtime. If you're on Node 16 or 17, or any environment that strips globals, a raw call to the OpenAI or Claude REST endpoint dies instantly:




// Breaks on Node < 18 with: ReferenceError: fetch is not defined
const res = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'x-api-key': process.env.ANTHROPIC_API_KEY,
'anthropic-version': '2023-06-01',
'content-type': 'application/json',
},
body: JSON.stringify({
model: 'claude-opus-4-8',
max_tokens: 256,
messages: [{ role: 'user', content: 'ping' }],
}),
});






fetch landed as a stable global in Node 18 and is unflagged from Node 21 onward. The fast diagnostic is a one-liner: node -e "console.log(typeof fetch)". If it prints undefined, you're below the line. The robust fix is to stop depending on the global and import explicitly so the code runs identically on old and new runtimes:




import { fetch } from 'undici'; // explicit, version-independent






The deeper trap here is the reverse assumption. People writing browser-side AI widgets sometimes paste in Node-only modules like import fs from 'node:fs' and ship it to a bundler. That doesn't throw at write-time; it throws at bundle time or as a blank white screen with Module "fs" has been externalized for browser compatibility. Failure 2 is really one symptom of a single root cause: you don't actually know which environment your code is executing in. That's what the diagnostic at the end solves.






Failure 3: tsconfig.json module vs moduleResolution — TypeScript compiles, Node refuses



This one is brutal because tsc reports zero errors and the failure only appears at runtime. You write TypeScript for a Claude tool-use loop, compile it, and Node throws ERR_MODULE_NOT_FOUND on a relative import that you can plainly see exists on disk.



The cause: with "module": "NodeNext", Node's ESM loader requires explicit file extensions in relative imports, but TypeScript lets you write extensionless paths in source. So this source:




import { buildTools } from './tools';






compiles to output that Node rejects, because at runtime the ESM resolver wants ./tools.js, not ./tools. The fix is to write the .js extension in your TypeScript source — yes, .js even though the file is .ts — and set the config to match:




{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"strict": true,
"outDir": "dist"
}
}









import { buildTools } from './tools.js'; // .js, resolved correctly by NodeNext






If instead your tool runs under a bundler (Vite, esbuild) or tsx, you want "moduleResolution": "Bundler" and you must not write extensions. The two configs are mutually exclusive, and copying a tsconfig.json from a Vite project into a plain-Node project is how this bug travels. The tell: tsc is green but node dist/index.js is red. Compilation success means nothing about runtime resolution.






Failure 4: An infinite retry loop that fires Claude/OpenAI calls back-to-back



This is a config failure with a cost, which is why it's the one to fear in an unattended side-hustle cron job. SDKs retry automatically on 429 and 5xx. If you also wrap the call in your own retry, and your own retry has no backoff and no max-attempt ceiling, a single transient error turns into a tight loop that re-fires the model call as fast as the network allows. On a billed API that's real money; on a rate-limited key it just gets you throttled harder.



Here is a retry wrapper that is actually safe — bounded attempts, exponential backoff with jitter, and it respects the Retry-After header the API sends back instead of guessing:




async function callWithBackoff(fn, { maxAttempts = 5, baseMs = 500 } = {}) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
const status = err?.status ?? err?.response?.status;
const retriable = status === 429 || (status >= 500 && status < 600);
if (!retriable || attempt === maxAttempts) throw err;

// Honor server's Retry-After (seconds) if present, else exponential.
const retryAfter = Number(err?.headers?.['retry-after']);
const wait = Number.isFinite(retryAfter)
? retryAfter * 1000
: baseMs * 2 ** (attempt - 1) + Math.floor(Math.random() * 250);

console.warn(`attempt ${attempt} failed (status ${status}); waiting ${wait}ms`);
await new Promise((r) => setTimeout(r, wait));
}
}
}






The config insight that makes this category disappear: if you're using an official SDK, it already retries (the Anthropic and OpenAI SDKs default to a small number of automatic retries with backoff). Adding your own naive loop on top is double-retrying. Either disable the SDK's retries via its constructor option and own the logic yourself, or keep the SDK's and don't add a loop. Pick one layer. The two-attempts-per-layer multiplication is what turns a 30-second blip into a runaway.






Failure 5: dotenv loaded too late, so the API key is silently undefined



The final one is an ordering bug that masquerades as an auth bug. With ESM, all import statements are hoisted and evaluated before any top-level code runs. So this looks right and is wrong:




import 'dotenv/config';            // looks like it runs first...
import { client } from './client.js'; // ...but this module's top-level code
// already read process.env at import time






If ./client.js constructs the Anthropic client at module top level (new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })), that read happens during the import graph evaluation, and depending on resolution order the key can still be undefined. The symptom is a 401 even though .env is sitting right there with a valid key — which sends people on a wild goose chase regenerating keys that were never the problem.



Two reliable fixes. Load env before the import graph using Node's --import flag, so it's guaranteed first:




node --import dotenv/config tool.js






or make the client construction lazy so it reads the env only when first called, long after dotenv has run:




let _client;
export function getClient() {
if (!_client) {
if (!process.env.ANTHROPIC_API_KEY) {
throw new Error('ANTHROPIC_API_KEY missing — check .env load order');
}
_client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
}
return _client;
}






That explicit throw is the unsung hero. A clear "key missing" error at the right layer saves you from debugging the API when the bug is in your file ordering.






The Node-vs-browser isolation script that ends the guessing



Every failure above traces back to one question: which environment is this code running in, and how is it reading modules? Run this once in any context — Node, a browser console, a bundler output, a GitHub Actions step — and it tells you the ground truth instead of you assuming:




function describeEnv() {
const report = {
runtime: 'unknown',
moduleSystem: typeof require === 'function' ? 'CommonJS-ish' : 'ESM-ish',
hasFetch: typeof fetch !== 'undefined',
hasWindow: typeof window !== 'undefined',
hasProcess: typeof process !== 'undefined',
};

if (report.hasWindow) report.runtime = 'browser';
else if (report.hasProcess && process.versions?.node) {
report.runtime = `node ${process.versions.node}`;
report.fetchExpected = Number(process.versions.node.split('.')[0]) >= 18;
}

console.table(report);
return report;
}

describeEnv();






Reading the output is a decision tree:





  • runtime says browser but you're importing fs/node:* → that's Failure 2's reverse; remove the Node-only module or move it server-side.


  • runtime is node 16.x and hasFetch: false → Failure 2; upgrade Node or import undici.


  • moduleSystem is CommonJS-ish but you wrote import statements → Failure 1; set "type": "module" or rename to .mjs.


  • hasProcess: true but your process.env key is empty → Failure 5; check dotenv load order with --import.



For GitHub Actions specifically, the runtime line is invaluable, because the runner's Node version often differs from your laptop's. A one-line workflow step pinning the version prevents the entire class of "works locally, fails in CI" reports:




- uses: actions/setup-node@v4
with:
node-version: '22'









The mental model to keep



None of these are AI-specific bugs — they bite every JavaScript project — but AI side-hustle tooling concentrates them because you're constantly pasting SDK examples from different eras and runtimes into one repo. The config files are the contract: package.json's type decides module system, tsconfig.json's module/moduleResolution decides how imports resolve, and your Node version decides which globals exist. When a copied script won't run, don't reread the logic — run describeEnv() and check those three contracts first. Ninety percent of the time the code was fine and the environment was lying to you.

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
1 Warnungen
title: Detect Exploitation - Why Your AI Side-Hustle Script Won't Run: 5 JavaScript Config Failures That Break Claude/OpenAI Tools (and How to Isolate Node v
id: 832239e4-4ac7-47c9-b619-c1f7921ddf4f
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-26
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-26"
        description = "YARA Signature for "
    strings:
        $str = "Why Your AI Side-Hustle Script" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("Why Your AI Side-Hustle Script Wont Run ")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*Why Your AI Side-Hustle Script Wont Run *"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "Why Your AI Side-Hustle Script Wont Run "
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Why Your AI Side-Hustle Script Won&#039;t Run.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Why Your AI Side-Hustle Script Won't Run: 5 JavaScript Config Failures That Break Claude/OpenAI Tools (and How to Isolate Node v

Thematisch verwandte Begriffe: Your, SideHustle, Script, Wont · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-88003 | InvoicePlane is a self-hosted open source application for managing invoi…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag