Category: Developer

  • “Unexpected token in JSON at position 0”: What It Means and How to Fix It

    “Unexpected token in JSON at position 0”: What It Means and How to Fix It

    “Unexpected token < in JSON at position 0” means the text you tried to parse isn’t JSON at all. It’s almost always an HTML page, usually a 404, a 500 error page or a login redirect, returned where your code expected JSON. Look at the raw response in your browser’s Network tab, fix the URL or the server error, and the message goes away.

    Newer versions of Chrome and Node word the same error differently: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Same cause, same fix. This guide covers that one first, then every other common JSON.parse error, with the exact messages a current Chrome shows.

    What “position 0” tells you

    The position is the character where the parser gave up, counting from 0. Position 0 means the very first character was wrong. Valid JSON can only start with {, [, ", a digit, -, true, false or null. So if the first character is <, you’re looking at HTML or XML.

    A position further along, like 26, means the start was fine and something broke in the middle. That’s usually a typo in the JSON itself (see the table below).

    Fixing “Unexpected token <“: find out what you actually received

    A typical line that triggers it:

    const res = await fetch('/api/users');
    const data = await res.json(); // 💥 Unexpected token '<'

    res.json() calls JSON.parse on whatever came back. To see what that was:

    1. Open DevTools (F12) → Network, reload, click the request and open the Response tab.
    2. Or log it in code before parsing:
      const res = await fetch('/api/users');
      const text = await res.text();
      console.log(res.status, res.headers.get('content-type'), text.slice(0, 200));

    You’ll almost always see one of these:

    • A 404 page: the URL is wrong. Look for a missing /api prefix, a typo, a relative path resolving against the wrong base, or a trailing slash the server treats differently.
    • A 500 error page: the server crashed and its framework returned an HTML error page. Fix the server error. The JSON problem is just a symptom.
    • A login page or redirect: your session expired or the request is missing its auth header or cookie, so the server sent you to sign in.
    • Your app’s index.html: in single-page apps, the dev server returns index.html for any unknown path. That usually means the API proxy isn’t set up, or the request went to the frontend’s port instead of the API’s.
    • A firewall or CDN challenge page: bot protection answered instead of your API.

    Make your code fail with a useful message

    const res = await fetch('/api/users');
    if (!res.ok) throw new Error(`HTTP ${res.status} from ${res.url}`);
    const type = res.headers.get('content-type') || '';
    if (!type.includes('application/json')) {
      throw new Error(`Expected JSON, got ${type}: ${(await res.text()).slice(0, 100)}`);
    }
    const data = await res.json();

    Now you get “HTTP 404 from https://…” instead of a confusing parse error.

    Want to test the endpoint on its own, outside your app? Send the same request in the ToolBrainy API Tester and look at the status code and raw body. Our guide on testing a REST API online walks through it.

    Every common JSON.parse error, and what causes it

    These are the exact messages from Chrome 139, which uses the same engine as Node.js. Firefox words them differently (“JSON.parse: unexpected character at line 1 column 1”), but the causes are the same.

    Error message What you passed Fix
    Unexpected token '<', "<!DOCTYPE "... is not valid JSON An HTML page Check the URL, status code and server logs (see above)
    Unexpected end of JSON input An empty string, or JSON cut off mid-way The response body was empty (often a 204 or an error) or truncated. Check that there’s a body before parsing.
    "undefined" is not valid JSON The value undefined The variable was never set, for example reading a localStorage key that doesn’t exist.
    "[object Object]" is not valid JSON An object that’s already parsed Don’t parse it again. It’s already JavaScript, or it was turned into a string without JSON.stringify.
    Expected property name or '}' in JSON at position 1 {'name': 'Ana'} or {name: "Ana"} JSON needs double quotes around every key and string.
    Expected double-quoted property name in JSON at position … {"a": 1, "b": 2,} Remove the trailing comma after the last property.
    Unexpected token ']', "[1, 2, 3,]" is not valid JSON A trailing comma in an array Remove the comma before ].
    Expected ',' or ']' after array element / Expected ',' or '}' after property value A missing comma, a comment, or truncated JSON JSON has no comments, so remove // and /* */. Otherwise, look for the missing comma at the position given.
    Unexpected non-whitespace character after JSON {"a":1}{"b":2} Two JSON values glued together, often from appending to a file or streaming. Wrap them in an array, or parse one value per line.
    Bad control character in string literal A real line break or tab inside a string Escape it as \n or \t, or build the JSON with JSON.stringify instead of by hand.
    Unexpected token 'N', "{"v": NaN}" is not valid JSON NaN or Infinity JSON has no NaN or Infinity. Use null or a string.
    Unexpected token '', "{…" is not valid JSON A file starting with an invisible byte order mark (BOM) Save the file as UTF-8 without BOM, or strip it with text.trim() (in JavaScript, trim() removes a BOM too).

    Find the exact spot in a big JSON file

    Newer error messages include the line and column, like (line 1 column 27), but minified JSON is all one line. The quickest way to find the problem is to paste it into the ToolBrainy JSON Formatter. It shows the parser’s error if the JSON is invalid. Once it’s valid, it formats the JSON with 2 spaces, 4 spaces or tabs so you can actually read it. It runs in your browser, so API responses and config files stay on your machine.

    Prevent it next time

    • Never build JSON by hand with string concatenation. Use JSON.stringify (or your language’s equivalent). It handles quotes, escapes and commas for you.
    • Check res.ok before calling res.json(). Most “Unexpected token <” errors are really HTTP errors in disguise.
    • Return JSON errors from your API. Configure your server to answer API routes with {"error": "…"} and a proper status code, not an HTML error page.
    • Wrap risky parses in try/catch when the input comes from users, files or localStorage.
    function safeParse(text, fallback = null) {
      try { return JSON.parse(text); }
      catch (e) { console.warn('Invalid JSON:', e.message); return fallback; }
    }

    Frequently asked questions

    What does “Unexpected token < in JSON at position 0” mean?

    The first character of the text you parsed was <, so it’s HTML, not JSON. Usually it’s an error page or a redirect returned by the server. Check the response in the Network tab.

    Why does it work in Postman but fail in my app?

    The requests are different. Compare the full URL, the method, the headers (especially Authorization and Content-Type) and the cookies. In the browser, a CORS preflight or a dev-server proxy can also change where the request actually goes.

    What does “Unexpected end of JSON input” mean?

    The parser ran out of text before the JSON was complete. Most often the response body was empty, for example a 204 No Content or an error with no body.

    Does JSON allow comments or trailing commas?

    No. Standard JSON allows neither. Some config formats (like JSON5, or the JSONC used in VS Code settings) do, but JSON.parse doesn’t accept them.

    How do I validate JSON online?

    Paste it into a JSON validator such as the ToolBrainy JSON Formatter. It shows the parser’s error if the JSON is invalid and formats it if it’s valid.

    The short version

    Position 0 and a < means you got HTML: check the URL, the status code and the auth. Any other position means the JSON itself is broken. Look for single quotes, trailing commas, comments or unescaped line breaks, and paste it into the JSON Formatter to see the error.

  • How to Test a REST API Online Without Installing Anything

    How to Test a REST API Online Without Installing Anything

    The first API bug that really cost me an afternoon was a 401 Unauthorized that had nothing to do with my credentials. The key was correct. The endpoint was correct. What was wrong was that I had typed Authorisation instead of Authorization in the header name, and the server did what servers do: it ignored the header it did not recognize and told me I was not logged in.

    I found it in about ten seconds once I stopped guessing and actually sent the request somewhere I could see every part of it. That is really all API testing is: making the invisible parts of a request visible so you can tell which one is wrong.

    What “testing an API” actually means

    When your app talks to an API, a lot happens that you never see. Your code builds a request, a library adds headers you did not write, the server responds, and somewhere in the middle something breaks. Testing an API means taking your application out of the loop entirely and sending the request by hand, so you can answer one question: is the problem my code, or is it the API?

    That single question saves enormous amounts of time. If the request works when you send it manually, the bug is in your code. If it fails the same way manually, the bug is in your request, your credentials, or the API itself. You have just cut the search space in half.

    Every request has exactly four parts

    No matter how complicated an API looks, every request you will ever send is built from the same four pieces. Once you can see all four, debugging gets much easier.

    1. The method. The verb: GET, POST, PUT, PATCH, or DELETE. It tells the server what kind of operation you want.
    2. The URL. The address of the resource, including any query parameters after the ?.
    3. The headers. Metadata about the request: what format you want back, who you are, what content you are sending.
    4. The body. The actual data you are sending, usually JSON. GET and DELETE normally have no body.

    A minimal request looks like this:

    GET https://api.github.com/users/octocat
    Accept: application/json

    You can send exactly that from the free online API tester without installing anything. Pick the method, paste the URL, hit send, and read what comes back.

    Picking the right method

    Using the wrong verb is one of the most common reasons a request fails with a confusing error. The convention is consistent across almost every REST API:

    • GET retrieves data and changes nothing. Safe to repeat.
    • POST creates something new. Repeating it usually creates duplicates.
    • PUT replaces a resource entirely with what you send.
    • PATCH updates only the fields you include.
    • DELETE removes the resource.

    The PUT versus PATCH distinction catches people out constantly. If you send a PUT with only one field, a strict API will treat every field you left out as deliberately blanked. When you only mean to change one thing, PATCH is almost always what you want.

    Reading the response

    The status code is the first thing to look at, and it tells you far more than most people use it for. The pattern is simple: 2xx means it worked, 4xx means you made a mistake, 5xx means the server made a mistake.

    • 200 OK — success, with data in the body.
    • 201 Created — your POST worked and something new exists.
    • 204 No Content — success, but there is deliberately nothing to return. Common after a DELETE.
    • 400 Bad Request — the server could not parse what you sent. Usually malformed JSON.
    • 401 Unauthorized — the server does not know who you are. Your credentials are missing, malformed, or expired.
    • 403 Forbidden — the server knows exactly who you are and you are not allowed. Do not go looking for a credentials bug here.
    • 404 Not Found — wrong URL, or the resource genuinely does not exist.
    • 422 Unprocessable Entity — your JSON was valid but the values failed validation. The body usually says which field.
    • 429 Too Many Requests — you hit the rate limit. Look for a Retry-After header.
    • 500 Internal Server Error — the API crashed. Not your fault, though a malformed request can sometimes trigger it.

    The 401 versus 403 distinction is worth committing to memory. It is the difference between “I don’t know you” and “I know you, and no.” People burn hours regenerating perfectly good API keys because they read a 403 as an authentication problem.

    Your first request, step by step

    Open the API tester and try a real public endpoint that needs no key:

    1. Set the method to GET.
    2. Enter https://api.github.com/users/octocat as the URL.
    3. Add a header: name Accept, value application/json.
    4. Send it.

    You should get a 200 back with a JSON object describing that account. Now change the username to something that does not exist and send it again. You will get a 404 with a short JSON body explaining the problem. That contrast, the same request succeeding and failing on one small change, is the whole debugging loop in miniature.

    Next, try sending data. This endpoint accepts test posts and echoes back what it received:

    POST https://jsonplaceholder.typicode.com/posts
    Content-Type: application/json
    
    {
      "title": "Testing an API",
      "body": "Sent from a browser.",
      "userId": 1
    }

    The Content-Type header matters here. Leave it off and many servers will not parse your body at all, then reject the request for missing fields you clearly sent. If the response comes back as a dense unreadable blob, paste it into the JSON formatter to expand it, and to confirm your own request body was valid JSON in the first place.

    Authentication: the three patterns

    Nearly every authenticated API uses one of three approaches, and all three are just headers.

    Bearer tokens are the most common. You send a token issued by the API:

    Authorization: Bearer YOUR_TOKEN_HERE

    The word Bearer, the single space, and the exact spelling of Authorization all matter. If the token is a JWT, you can check what is inside it and whether it has already expired using the JWT decoder. An expired token is a very common cause of a sudden 401 on code that worked yesterday.

    API keys come in a custom header, and the name varies by service:

    X-API-Key: YOUR_KEY_HERE

    Some APIs instead want the key as a query parameter. Check the docs, because guessing wastes time.

    Basic auth sends a username and password Base64-encoded. It is the oldest pattern and still turns up in internal and legacy systems. If you need to build or inspect one by hand, the Base64 encoder handles the encoding step. Worth remembering: Base64 is encoding, not encryption, so Basic auth over plain HTTP is effectively sending your password in the clear.

    Why the browser sometimes blocks you

    This is the one thing that confuses people testing APIs from a browser, and it is worth understanding rather than fighting.

    Browsers enforce CORS, a security rule that stops a page on one domain from freely reading responses from another domain. If the API does not explicitly send back a header saying “requests from other origins are allowed,” the browser refuses to hand you the response, even though the server answered perfectly well.

    The important part: a CORS error is not an API error. The request usually succeeded. The browser is simply declining to show you the result. Signs you are looking at CORS rather than a real failure:

    • The status shows as 0, or there is no status at all.
    • The error mentions “origin”, “cross-origin”, or “Access-Control-Allow-Origin”.
    • The same URL works fine when you open it directly in a new tab.

    Public APIs designed for browser use, like the two in the examples above, send the right headers and work fine. Internal APIs and many paid services do not, by design. For those, test from a server-side tool or ask whoever runs the API to allow your origin.

    The errors you will actually hit

    • 401 on a key you know is right → check the header name spelling and that the word Bearer is present with one space after it.
    • 400 on a body that looks fine → run it through a JSON validator. A trailing comma or a smart quote from a word processor will do it.
    • Server ignores your data → you almost certainly forgot Content-Type: application/json.
    • 404 on a URL you copied from the docs → check for a missing or doubled slash, or a version prefix like /v1/ you left out.
    • Query parameters not working → special characters need escaping. The URL encoder shows you what your parameters really look like once encoded.
    • Worked yesterday, 401 today → the token expired. Decode it and check the expiry claim.

    Habits that save time

    • Change one thing at a time. When you edit the URL, the headers, and the body together, a fix and a new bug cancel out and you learn nothing.
    • Get a GET working first. Prove your credentials work on a simple read before debugging a complex write.
    • Read the response body, not just the status. Most APIs explain the exact problem there, and most people never scroll down to it.
    • Never paste production tokens into tools that send them to a server. A live token is a working key to your account until it expires. Prefer tools that run in your browser.

    Frequently asked questions

    Can I test an API without installing Postman?

    Yes. A browser-based tool like the free online API tester sends the same requests with no install and no account. That makes it the practical option on a locked-down work laptop or a Chromebook where you cannot install desktop software.

    What is the difference between 401 and 403?

    A 401 means the server could not identify you: your credentials are missing, malformed, or expired. A 403 means it identified you successfully and you do not have permission for that action. Regenerating your API key will not fix a 403.

    Why do I get a CORS error when the API works in a browser tab?

    Opening a URL directly is not a cross-origin request, so CORS does not apply. Sending it from a page on a different domain is, and the browser will block you from reading the response unless the API sends an Access-Control-Allow-Origin header. The request itself usually succeeded.

    Should I use PUT or PATCH to update something?

    Use PATCH when you want to change specific fields and leave the rest alone. Use PUT when you are replacing the whole resource. Sending a partial PUT can blank out every field you omitted.

    Is it safe to test with a real API key?

    Only in tools that keep the key on your device rather than sending it to their own server, and ideally with a development or read-only key rather than a production one. Rotate any key you have pasted somewhere you are unsure about.

    Why does the server ignore the data I send?

    Almost always a missing Content-Type: application/json header. Without it many frameworks never parse the body, so the server behaves as though you sent nothing and complains about missing required fields.

    The short version

    Testing an API is just sending the four parts of a request by hand so you can see which one is wrong. Read the status code first, then the response body. Remember that 401 and 403 are different problems, that a missing Content-Type silently discards your data, and that a CORS error is the browser talking, not the API. Get a simple GET working before you debug anything complicated, and change one variable at a time.

    When something breaks, open the API tester, rebuild the request piece by piece, and let the response tell you where the problem is. It is usually a header, and it is usually spelled slightly wrong. You can find the rest of the developer tools in one place when you need them.

  • Understanding JWT Tokens: A Beginner’s Guide

    Understanding JWT Tokens: A Beginner’s Guide

    The first time I really looked at a JWT, I was staring at a login bug at 11 PM, copying a long, dot-riddled string out of a browser’s dev tools and wondering what on earth it said. If you’ve seen a value shaped like xxxxx.yyyyy.zzzzz in a cookie or an Authorization header and felt the same way, this is the explanation I wish I’d had that night.

    What a JWT is for

    A JWT is a compact, self-contained way to carry claims: pieces of information such as “this is user #4521” or “this token expires at 3 PM.” When you log in, the server hands you a token; your browser sends it back with each request, and the server can trust it without looking anything up in a database. That statelessness is what makes JWTs so popular for APIs.

    The three parts of a token

    Every JWT has three sections separated by dots. Each is Base64URL-encoded (not encrypted; more on that below):

    1. Header. Says what type of token it is and which signing algorithm was used (for example, HS256 or RS256).
    2. Payload. The actual claims: user ID, roles, issued-at time, and expiry. These are readable by anyone who has the token.
    3. Signature. A cryptographic stamp created from the header, payload, and a secret key. It lets the server verify the token hasn’t been tampered with.

    The most important thing to understand: it’s signed, not secret

    This trips up almost every beginner. The payload of a JWT is encoded, not encrypted. Anyone who intercepts the token can read its contents: the user ID, roles, and any other claims are all visible.

    What the signature guarantees is integrity: nobody can change the payload without the secret key, because doing so would break the signature. So the rule is simple: never put sensitive data (passwords, card numbers, secrets) inside a JWT payload. Treat it as public information that merely can’t be forged.

    How verification works

    When the server receives a token, it recomputes the signature from the header and payload using its secret key, then compares it to the signature in the token. If they match, the token is authentic and untampered. If they don’t, it’s rejected. The server also checks the expiry claim so old tokens stop working automatically.

    Inspecting a token safely

    When debugging authentication, you often need to peek inside a token to check its claims or expiry. You can decode the header and payload with our JWT decoder: paste the token and it splits and decodes the parts for you, right in your browser.

    Two safety notes when decoding tokens:

    • Use tools that decode locally. Because a token can be used to impersonate you until it expires, never paste a live production token into a random site that sends it to a server. A browser-based decoder that keeps the token on your device is far safer.
    • Decoding is not verifying. A decoder shows you what’s inside, but only the server with the secret key can prove a token is genuine.

    If you’re curious about the encoding underneath, each section is Base64URL, the same family covered in our guide to Base64 encoding.

    Common beginner mistakes

    • Storing secrets in the payload: remember, it’s readable by anyone.
    • Never setting an expiry: a token that lives forever is a security hole if it leaks.
    • Trusting a token without verifying the signature: always verify server-side.
    • Using a weak signing secret: a short, guessable secret undermines the whole system.

    Questions developers actually ask

    Is the data in a JWT encrypted?

    No. The header and payload are only encoded and can be read by anyone. The signature prevents tampering, not reading.

    Can a JWT be tampered with?

    Not without being detected. Changing any part invalidates the signature, so the server will reject it, provided the server actually verifies the signature.

    Where should I store a JWT in the browser?

    It’s a trade-off. HttpOnly cookies protect against cross-site scripting but need CSRF protection; local storage is simpler but exposed to scripts. Choose based on your app’s threat model.

    What does an expired token do?

    A well-built server checks the expiry claim and rejects tokens past their time, forcing a fresh login or token refresh.

    If you remember three things

    Keep secrets out of the payload, always set an expiry, and always verify the signature on the server. A JWT is just a signed note that says who you are and when it stops being valid, nothing more mysterious than that. Next time one lands you in a late-night debugging session, drop it into the JWT decoder and read exactly what it’s telling you.

  • What Is Base64 and When Should You Use It?

    What Is Base64 and When Should You Use It?

    Spend enough time near web development, email, or APIs and you’ll keep running into Base64: those long strings of what look like random letters and numbers, often with an = or two on the end. It looks like something you’re not supposed to read. It isn’t, and the idea behind it is refreshingly simple once someone spells it out.

    What Base64 actually is

    Base64 is a way of representing binary data (like an image, a PDF, or raw bytes) using only 64 “safe” text characters: A–Z, a–z, 0–9, and the symbols + and /. The = characters you sometimes see at the end are padding to keep the length consistent.

    The key thing to understand: Base64 is an encoding, not encryption. It doesn’t protect or hide your data; anyone can decode it instantly. Its only job is to convert data into a form that survives systems designed to handle plain text.

    Why it exists

    Many older but still-essential systems, email being the classic example, were built to transmit text, not arbitrary binary bytes. Send raw binary through them and certain byte values get mangled or interpreted as control characters, corrupting the data.

    Base64 solves this by translating binary into text that any text-based channel can carry safely. The trade-off is size: Base64 data is roughly 33% larger than the original binary, because it uses text characters to represent every few bytes.

    Where you’ll actually see it

    • Email attachments. Under the hood, that PDF you attached is Base64-encoded so it can travel through email servers intact.
    • Embedding images in HTML/CSS. A small icon can be embedded directly as a data: URI using Base64, saving an extra network request.
    • APIs and JSON. Since JSON is text, binary payloads (like a file upload or a small image) are often Base64-encoded to fit inside a JSON field.
    • Basic authentication headers. HTTP Basic Auth encodes username:password in Base64, which is exactly why Basic Auth must always be used over HTTPS, since Base64 offers no security at all.
    • Storing binary in text-only fields. Config files, tokens, and certificates are frequently Base64-encoded.

    When you should (and shouldn’t) use it

    Use Base64 when you need to move or store binary data through something that only accepts text, or when embedding a tiny asset inline is worth avoiding an extra request.

    Don’t use Base64 when:

    • You need security. It hides nothing. Use real encryption instead.
    • You’re embedding large images. The 33% size penalty and loss of browser caching usually make a normal image file the better choice.
    • Plain binary transfer is already supported; encoding then adds size for no benefit.

    Encoding and decoding it yourself

    You rarely need to do this by hand, but it’s handy when debugging. To quickly encode text or decode a mysterious-looking string, use our Base64 encoder and decoder: paste your value in, and it converts instantly in your browser.

    One related tip: if you’re inspecting a token that contains Base64 sections, such as a JSON Web Token, a dedicated JWT decoder will split and decode it more usefully than a plain Base64 tool.

    Base64, quick answers

    Is Base64 secure?

    No. It’s trivially reversible and provides zero protection. Never treat Base64 as a way to hide passwords or sensitive data.

    Why does Base64 make files bigger?

    It represents every 3 bytes of binary data with 4 text characters, which adds about one third to the size.

    What do the = signs mean?

    They’re padding, added so the encoded output length is always a multiple of four characters. They carry no data themselves.

    Can I decode any Base64 string?

    If it’s valid Base64, yes, but the result only makes sense if you know what the original data was (text, an image, etc.).

    The one-sentence version

    Think of Base64 as a translator rather than a lock: it makes binary data safe to travel through text-only systems, and it charges you about a third more size for the trip. Reach for it when you need compatibility, never when you need confidentiality. Got a string to encode or decode this second? The Base64 tool is right here.